You are not logged in.

#1 2005-04-01 20:47:03

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

libsigc++ troubles (long)

ok, warning... C programmers won't be able to answer this - it's a template thing:

I'm using libsigc++ and trying to get the following functionality:

void foo(int i) { std::cout << "called i=" << i << std::endl; }
...
signal_creator sc;
sc.create<void,int>("signal-one"); //create internal sigc::signal<void,int>();
sc.create<void>("signal-two"); //create internal sigc::signal<void>();

sc.connect("signal-one",sigc::ptr_fun(foo));
sc.connect("signal-two",sigc::bind(sigc::ptr_fun(foo),5));

sc.emit("signal-one",2);
sc.emit("signal-two");

the creation is easy... but in order to store these name-based internally, it'd have to use a map against the signal_base class... which is going to get real odd when calling connect and emit... for the record, the internal map is: std::map<std::string,sigc::signal_base> _signals; and signal_iterator is just a typedef of the map's iterator...

when calling connect, I'd like to enforce that the passed in slot type will match the signal type... this is messy (note, the slot and signal templates are for convienance... I'm taking advantage of those here...)

template <typename Tret, typename T1=sigc::nil, typename T2=sigc::nil,
        typename T3=sigc::nil, typename T4=sigc::nil, typename T5=sigc::nil,
        typename T6=sigc::nil, typename T7=sigc::nil>
bool connect(const std::string& name, sigc::slot<Tret,T1,T2,T3,T4,T5,T6,T7>& slt)
{
    signal_iterator it = _signals.find(name);
    if(it != _signals.end())
    {
        (sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7>(it->second))
            .connect(slt);
        return true;
    }
    return false;        
}

ok, that's fine and dandy, but we got a c-style cast in there... which isn't going to enforce the type similarity at compile time... a dynamic_cast would work to enforce it at runtime, but that's not what I'm looking for...

The emit function is similar, however there are 8 versions of it, stepping down the args from 7 to 0 and using the proper sigc::nil based template type to call the proper emit...

Both the emit and connect functions would most likely seg fault at run time if the types weren't matching... which isn't good.  I'm looking for a way to enforce this at compile time... as far as I can tell, there's no way to conecpt-check this due to the storing of the base class....
another option would be to maintain 7 maps... signals0, signals1, signals2 etc... similar to the sigc:: implementation... that way, at least the argument numbers would be guarenteed to match at runtime... however that still does not allow for type matching...

what I'm looking for is some way to do this... to go back to the original example at the top, I'd like the following:

bool bar(int a, int b, int c) { return (a != b != c); }
...
sc.connect("signal-one",sigc::ptr_fun(bar));
sc.emit("signal-one",2,5);

to fail at compile time... because, of course, none of the types match... if anyone can give me any pointers, I'd love you forever

Offline

#2 2005-04-01 21:02:00

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: libsigc++ troubles (long)

Another thing which comes to mind would be using a wrapper instead of signal_base.... that is, a class which would contain the signal_base, but also some indicator as to parameter count... hmm template specialization may be able to help there.... I'll have to think

Offline

#3 2005-04-04 21:58:50

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: libsigc++ troubles (long)

so, I've determined a solution to my problem... there is a very easy fix for this, which is using dynamic_cast which will throw a bad_cast exception (except in the case of pointer, it returns NULL on a bad cast).  The problem with using dynamic_cast is that you need to compile in RTTI which adds boatloads of unused crap to each object (type identification stuff).  This would hinder usage because the RTTI would only be used in a small percentage of the code, yet all objects would incure the penalty.

So instead I was trying to get around using dynamic_cast through different metaprogramming tricks... finally I decided on the simplest solution ever... a #ifdef block....

It's so simple and I feel like a fool...
the innards of the connect function in the first post will become:

#ifdef DYNAMIC_SIGNAL_CHECK
try
{
    dynamic_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> >
        (it->second)).connect(slt);
}
catch(bad_cast& err)
{  //custom error
     throw runtime_error("slot does not match signal [" + (it->first) +"]");
}
#else
    static_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> >
        (it->second)).connect(slt);
#endif

Offline

#4 2005-04-06 11:48:55

STiAT
Member
From: Vienna, Austria
Registered: 2004-12-23
Posts: 606

Re: libsigc++ troubles (long)

I've been considering same, but to be true, i didn't dare to post you could signal check the cast... i read the code several times, and came to the irritating idea that you're definitely too good in what you do as that the solution could be that simple.

RTTI isn't always bad, especially not for casts. As well as if you have RTTI enabled, you can cast over two seperate derive-trees. I'm most likely segfaulting at this "unclean" things which some other developers are using in our company... after this, i'm often stuck for hours cleaning up their code...

Another thing .. when thinking about this, the following came up in my brain:

static_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> > 

Couldn't this cause segfaults as well? Most likely, DYNAMIC_SIGNAL_CHECK will be defined, but otherwhise, wouldn't this segfault?

Maybe next time
// STi


Ability is nothing without opportunity.

Offline

#5 2005-04-06 15:30:22

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: libsigc++ troubles (long)

STiAT wrote:

RTTI isn't always bad, especially not for casts. As well as if you have RTTI enabled, you can cast over two seperate derive-trees. I'm most likely segfaulting at this "unclean" things which some other developers are using in our company... after this, i'm often stuck for hours cleaning up their code...

I agree, RTTI is a good thing, but I would only be using RTTI in one small class and all the others (including all STL classes) would incur the penalty... if I was doing more RTTI stuff, it'd be warranted, but I didn't feel so here.

STiAT wrote:

Another thing .. when thinking about this, the following came up in my brain:

static_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> > 

Couldn't this cause segfaults as well? Most likely, DYNAMIC_SIGNAL_CHECK will be defined, but otherwhise, wouldn't this segfault?

Yeah, it'll segfault, but I am assuming that DYNAMIC_SIGNAL_CHECK would be defined for testing, and once it's all found to work properly, you can undef it and the static_cast will work fine (of course you don't *need* to undefine it, if one feels safer that way, which is whay I didn't use a #ifdef DEBUG block)

Offline

#6 2005-04-06 19:14:40

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: libsigc++ troubles (long)

so I finally decided on using my own cast, to simplify my code (and remove the 20 or so #ifdefs...)

template <typename Tret, typename T1=sigc::nil, typename T2=sigc::nil,
   typename T3=sigc::nil, typename T4=sigc::nil, typename T5=sigc::nil,
   typename T6=sigc::nil, typename T7=sigc::nil>
inline sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> signal_cast(const sigc::signal_base& sig)
{
#ifdef DYNAMIC_SIGNAL_CHECK
   try
   {
      return dynamic_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> >(sig);
   }
   catch(std::bad_cast& bc)
   { //only catch bad_cast... if something else happens, by chance, we'll bubble it upward
      throw std::runtime_error("bad_cast: signal type does not match calling type");
   }
#else
   return static_cast<sigc::signal<Tret,T1,T2,T3,T4,T5,T6,T7> >(sig);
#endif
}

Offline

Board footer

Powered by FluxBB