6

I'm trying to create a simple Signals/Slots system in C++ without boost but I've got some problems when I try to use it with parameters, here is my code :

My Signal class :

template <typename T> 
class Signal
{
private:
    typedef std::function<T> Slot;

public:
    Signal();

    void connect( Slot slot );
    void emit( T data );
    void emit();

private:    
    std::vector<Slot> slots;    
};

My Test class :

class Object
{
public:
    Object();
    void sayHello(  int i  );
};

So, I use my class like that :

Signal<void(int)> signal;
signal.connect( std::bind( &Object::sayHello, player, std::placeholders::_1 ) );
signal.emit( 0 );

I've got a warning in Signal.cpp : Candidate function not viable: no known conversion from 'void (*)(int)' to 'int' for 1st argument;

In this code :

template <typename T>
void Signal<T>::emit( T data )
{
    typename std::vector<Slot>::iterator i;
    Slot t;

    for( i = this->slots.begin(); i != this->slots.end(); i++ )
    {
        t = (*i);
        t( data );   // Here
    }
}

How can I solve that? If I want give an object or multiples parameters to my "emit" method, hoc can i do?

Thanks!

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Dono
  • 522
  • 7
  • 11
  • Is [this](http://liveworkspace.org/code/2d733163b1af460eedcf05a7fb760608) what you were trying for? You're specifying that `T` == `void(int)`, but `emit` seems to expect that `T` == `int`. – ildjarn Nov 07 '12 at 20:49
  • Oups! Thanks, that work better with that indeed :) – Dono Nov 07 '12 at 21:21

1 Answers1

6

Picking three lines from your Signal class:

template <typename T> 
    // ...
    typedef std::function<T> Slot;
    // ...
    void emit( T data );

And the declaration of your signal:

Signal<void(int)> signal;

The answer should be pretty obvious: void emit(T) becomes void emit(void(*)(int)) (function types like void(int) transform to pointers when they are function parameters).

I'd simply recommend going the same way every class template that uses T<Signature> goes: partial specialization.

template<class Sig>
class Signal;

template<class R, class... Args>
class Signal<R(Args...)>{
  // ...
  typedef std::function<R(Args...)> Slot;
  // ...
  void emit(Args... args) const{
    // iterate over slots and call them, passing `args...`
  }
};
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • `emit` doesn't seem to use the return type for anything anyway, so maybe `void` should be assumed? – ildjarn Nov 07 '12 at 20:50
  • @ildjarn: I'm still pondering what the best course of action would be here. Assuming `void` is one of them, but doesn't change my answer by much. I'm also thinking about a combinator parameter that is optionally passed to `emit` and either combines the result values (like folding) or passes them on to somewhere. – Xeo Nov 07 '12 at 20:53
  • Oh thanks, I've learnt many things here and that work perfectly :D. I will read more about variables args with template in c++ so. – Dono Nov 07 '12 at 21:23