0

I have a class that needs to support a dynamic set of type arguments, but VS2012 does not support variadic templates. (VS2013 and the compiler CTP do support variadic templates, but I can't use them. Nor can I use Boost.)

So, I'm trying to find a solution by using "templates specialization". Below is what I have so far. (Notice that if you rename Signaler2 by Signaler... there are some compilation issues.)

Any ideas on how to solve this problem?

#include <functional>
#include <vector>

using namespace std;

namespace spectralCore
{

#pragma region Signaler2<T1, T2>

// A signal object to handle signal/events notifications.
template<typename T1, typename T2>
class Signaler2
{
public:
  typedef std::function<void (T1,T2)> Func;

public:
  void Call(T1 arg1, T2 arg2)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)(arg1, arg2);

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)(arg1, arg2);
  }

  void operator ()(T1 arg1, T2 arg2)
  {
    Call(arg1, arg2);
  }

  Signaler2& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler2& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (T1,T2)>() == f.template target<void (T1,T2)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler2& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler2& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (T1,T2)>() == f.template target<void (T1,T2)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template <void (T1,T2)>() == f.template target<void (T1,T2)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;
  std::vector<Func> _postHandlers;
};

#pragma endregion

#pragma region Signaler<T1>

// A signal object to handle signal/events notifications.
//template<typename T1> class Signaler<T1,void>
template<typename T1>
class Signaler
{
public:
  typedef std::function<void (T1)> Func;

public:
  void Call(T1 arg)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)( arg );

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)( arg );
  }

  void operator ()(T1 arg)
  {
    Call( arg );
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#pragma endregion

#pragma region Signaler<void>

// A signal object to handle signal/events notifications.
template<>
class Signaler<void>
{
public:
  typedef std::function<void (void)> Func;

public:
  void Call()
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)();

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)();
  }

  void operator ()()
  {
    Call();
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back( f );
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
      {
        _postHandlers.erase( i );
        break;
      }
    }

    return *this;
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ( (*i).template target<void (void)>() == f.template target<void (void)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#pragma endregion

}

Notices that I have also try the following definition :

template<typename T1, typename T2=void> class Signaler
{ ... }

template<typename T1> class Signaler<T1,void>
{ ... }

template<> class Signaler<void,void>
{ ... }

But I got a "LINK" error :

error LNK2001: unresolved external symbol "public: static class Signaler ShadingSystem::Signal_ShaderUpdated" (?Signal_ShaderUpdated@ShadingSystem@2V?$Signaler@PEAVShader@@@spectralCore@@A) D:\spectralGraph.lib(VNScene.obj)

Spectral
  • 717
  • 2
  • 12
  • 28
  • What Microsoft used to do to mimic variadic templates in the STL was to include the same header multiple times with some macros defined differently so that it would expand into templates with a different number of arguments. It's ugly, but you might be able to use the same trick. – Trillian Dec 14 '13 at 14:51
  • Thanks. Sure it is a way to do. But first I have to be able to define it by hand... it is what I try to do right now ! Once I have this code compiling... I will be able to work on a macro ;-) – Spectral Dec 14 '13 at 16:52
  • 1
    I don't think the line `(*i).template target() == f.template target()` does what you think it does. You can't check `std::function` for equality in the way you're using it - you need seperate tokens. – Xeo Dec 14 '13 at 21:44
  • 1
    http://stackoverflow.com/questions/7683041/how-to-implement-variadic-template-with-pre-c0xvs2008 – user2485710 Dec 14 '13 at 22:04

1 Answers1

1

From this question, it seems that templates cannot be overloaded on their number of arguments in C++98. However, you can use the suggested trick where you define the base template as having the maximum number of supported template arguments and then have specializations when the tail template arguments are void. If you combine this with the repeated header inclusion with macro expansion trick, you get something like this:

Signaler.h:

#include <functional>
#include <vector>

template<typename T1 = void, typename T2 = void, typename T3 = void, typename T4 = void, typename T5 = void>
class Signaler
{
private:
  Signaler();
};

#define SIGNALER_EXPAND_TYPES(macro) 
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

#define SIGNALER_EXPAND_TYPES(macro) macro(T1)
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

#define SIGNALER_EXPAND_TYPES(macro) macro(T1), macro(T2)
#include "SignalerSpecialization.h"
#undef SIGNALER_EXPAND_TYPES

// Same for 3, 4 and 5 args

SignalerSpecialization.h:

#define SIGNALER_EXPAND_TYPE(T) T
#define SIGNALER_TYPE_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_TYPE)

#define SIGNALER_EXPAND_TEMPLATE_ARGUMENT(T) typename T
#define SIGNALER_TEMPLATE_ARGUMENT_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_TEMPLATE_ARGUMENT)

#define SIGNALER_EXPAND_PARAMETER(T) T arg##T
#define SIGNALER_PARAMETER_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_PARAMETER)

#define SIGNALER_EXPAND_ARGUMENT(T) arg##T
#define SIGNALER_ARGUMENT_LIST SIGNALER_EXPAND_TYPES(SIGNALER_EXPAND_ARGUMENT)

template<SIGNALER_TEMPLATE_ARGUMENT_LIST>
class Signaler<SIGNALER_TYPE_LIST>
{
private:
  typedef std::function<void (SIGNALER_TYPE_LIST)> Func;

public:
  void Call(SIGNALER_PARAMETER_LIST)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      (*i)(SIGNALER_ARGUMENT_LIST);

    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
      (*i)(SIGNALER_ARGUMENT_LIST);
  }

  void operator()(SIGNALER_PARAMETER_LIST)
  {
    Call(SIGNALER_ARGUMENT_LIST);
  }

  Signaler& operator*=(Func f)
  {
    _postHandlers.push_back(f);
    return *this;
  }

  Signaler& operator/=(Func f)
  {
    for(auto i = _postHandlers.begin(); i != _postHandlers.end(); i++)
    {
      if ((*i).template target<Func>() == f.template target<Func>())
      {
        _postHandlers.erase(i);
        break;
      }
    }

    return *this;
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back(f);
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ((*i).template target<void (SIGNALER_TYPE_LIST)>() == f.template target<void (SIGNALER_TYPE_LIST)>())
      {
        _handlers.erase(i);
        break;
      }
    }

    return *this;
  }

  bool IsRegistered(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
      if ((*i).template target<void (SIGNALER_TYPE_LIST)>() == f.template target<void (SIGNALER_TYPE_LIST)>() )
        true;

    return false;
  }

private:
  std::vector<Func> _handlers;     // First step handlers
  std::vector<Func> _postHandlers; // Second step handlers
};

#undef SIGNALER_EXPAND_TYPE
#undef SIGNALER_TYPE_LIST

#undef SIGNALER_EXPAND_TEMPLATE_ARGUMENT
#undef SIGNALER_TEMPLATE_ARGUMENT_LIST

#undef SIGNALER_EXPAND_PARAMETER
#undef SIGNALER_PARAMETER_LIST

#undef SIGNALER_EXPAND_ARGUMENT
#undef SIGNALER_ARGUMENT_LIST

PS: Your overloading of the *= and /= operators is of very poor taste.

Community
  • 1
  • 1
Trillian
  • 6,207
  • 1
  • 26
  • 36
  • Hi, I have try another definition (see the question please) but I got a LINK error ! Does someone has an idea to solve the problem ? Thx – Spectral Dec 19 '13 at 13:47
  • BTW, Trillian, your example does not compile :'( – Spectral Dec 19 '13 at 14:19
  • @Spectral Oops, it was missing the STL #includes, it should work now. As for your link error, it is about your ShadingSystem class, not the Signaler class. – Trillian Dec 19 '13 at 16:13
  • thanks but here is the kind of error I get : "error C2248: 'Signaler::Signaler' : cannot access private member declared in class 'Signaler' DependencyProperty.cpp". The error occur in the DependencyProperty constructor. Here is the definition of the signal variable in the DependencyProperty class : "Signaler Signal_PropertyChanged;". Thanks – Spectral Dec 20 '13 at 11:37