Paul Winalski wrote in <CABH=_VT5Yi-6uRz_-045cmf-4kbgTSHZhsCLLracoFXNDwS\
AYQ(a)mail.gmail.com>:
|On 8/24/18, Steffen Nurpmeso <steffen(a)sdaoden.eu> wrote:
|> Though even more unfortunate i am, since this union trick is often
|> the only way to be able to do proper a.k.a. allowed type
|> conversion, where the standard text forbids something quick and
|> easy, casting of or to function pointers in C++ comes to mind
|> spontaneously. (The linked story also tries to go via (char*) to
|> a desired type, but the compiler seems to be too neat. And even
|> if this is a bug, of course...)
|
|One of the complaints about DEC's C99 compiler for Ultrix was that it
|was too didactic in its enforcement of the C99 standard. One customer
|called it the Rush Limbough of compilers, because it was extremely
|conservative and you couldn't argue with it.
|
|Function pointers can be tricky, as they might not be simple pointers
|to a sequence of instructions. On Itanium, for example, a C
|pointer-to-function points to a descriptor for the function, not the
|function code itself.
They can be as tricky as they want as long as i can go and use
them, wouldn't you agree? Not only for simple callbacks but for
"manual VTables" you simply need those dynamic callbacks. Like
you have said, you need a way to change the instruction location.
As far as i know this is what actually happens, and i want to be
able to access this mechanism without being prevented from some
language rule which is backed by nothing. That is all. Whatever
high language construct there may be. To me this was one of the
properties of the C language.
I can give you another example. I have a C++ library which uses
an approach to events where you (can) define slots, as in
pub Misc::Sender<IOEvent> onInput;
You then say XY.onInput.connect(MYHOOK), where MYHOOK can be one
of four different types, which return "event consumed:
pub typedef boolean ( *Slot1)(Event *);
pub typedef boolean ( *Slot2)(void *, Event *);
pub typedef boolean ( EventListener::*Slot3)(Event *);
The first is a PTF callback without user provided data, the second
adds that; the third is a PointerToMember. The third defines
a PTM to a subclass of EventListener, which is an empty class but
with VTable (protected destructor), provided for the sole purpose
of offering a cheap event slot without marshalling object, managed
by an equal number of connection types, superclass:
// actual connection structures
// note we use the lower three bits of Conn::flags for Flags (including
// ConnType) as below. this works because the memory cache has an
// alignment of 8 (or 16, and only).
pri struct Conn{
union{
uir flags;
Conn *right;
};
} SF_CC_PACKED;
E.g., the third
pri struct ConnML{ // EventListener PTM
Conn base;
EventListener *elobj;
Slot3 slot;
} SF_CC_PACKED;
So far so good. But what to do with free-form PTM hooks?
You need templates for that:
pub template<class OBJ>
struct ConnMT{ // free-form PTM (template indirection)
typedef boolean (OBJ::*Slot4)(Event *);
ConnFO cfo;
OBJ *tobj;
Slot4 slot;
} SF_CC_PACKED;
But that induces several problems, one of them is
- 2.95.2 and 2.95.3 compile the library without errors.
The problem with them is that they are not able to handle the
free-from template pointer-to-member (PTM),
as well as the EventListener PTM specialization of
SF::Sys::Misc::Sender correctly.
This is a no-go for other libraries, which use events as
a key concept (and base upon EventListener to get rid of the
purely template PTM based connection overhead), but is
otherwise no problem.
(Some of the tests in test/ will not compile, though.)
(Note: 2.95.2 has not been tested for a long time.
2.95.3 is part of the test-suite at the time of this writing
aka TAG 0.6.0.)
The other is that you cannot store ConnMT in generic code -- the
C++ standard does not offer this possibility, even though Slot4 is
a PTM just the very same way that Slot3 is, right? PTMs are
a wild mix of VTable and offset, right. But luckily it can be
done, with a very wild hack, but which still works after almost
twenty years, whether gcc or clang is used, at least the tests
ran just fine last time i have tried:
// disconnecting of those is hard; try it like that (doc/cppcreq.txt).
pri struct ConnMX{
ConnFO cfo;
void *tobj;
ui1 ptmbuf[szof(Slot3)];
} SF_CC_PACKED;
Nonetheless you need marshalling objects to dispatch events
through such slots, which is very painful and expensive, as well
as completely unnecessary yet imposed solely by language speech,
but at least all the list handling and such works just fine
(though using memcmp on ptmbuf is really wild).
_pro template<class OBJ>
void _Sender::connect4(typename ConnMT<OBJ>::Slot4 _Slot,
OBJ *_tobj) const{
ConnMT<OBJ> *cmt;
cmt = SF_talloc(ConnMT<OBJ>, 1);
cmt->cfo.base.flags = ((R(uir,m_slots) & ~f_mask) | ct_ptm_t);
m_slots = &cmt->cfo.base;
cmt->cfo.slot = &_Sender::_Dispatch<OBJ>;
cmt->cfo.obj = cmt;
cmt->tobj = _tobj;
cmt->slot = _Slot;
}
_pri template<class OBJ>
_sta boolean _Sender::_Dispatch(void *_tobj, Event *_ev){
ConnMT<OBJ> *cmt = S(ConnMT<OBJ>*,_tobj);
return (cmt->tobj->*cmt->slot)(_ev);
}
And of course this is a primitive event mechanism which requires
an Event class to be used.
--steffen
|
|Der Kragenbaer, The moon bear,
|der holt sich munter he cheerfully and one by one
|einen nach dem anderen runter wa.ks himself off
|(By Robert Gernhardt)