Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing references to Variadic templates

I'm working on an Event library and I'm facing a problem with Variadic templates.

All is working very nice except the fact that I can't pass references as parameters...

Here is a very simplified example wrote to expose my problem.

struct DelayedSignal 
{   
    ~DelayedSignal ()
    { std::cout << "~DelayedSignal CLOSE" << std::endl; }

    template<class C, class... Args>
    DelayedSignal ( void(C::*func)(Args...), C& obj )
    { std::cout << "DelayedSignal INIT - 03 - pointer to method & pointer to class instance (Arg num: " << sizeof...(Args) << ")" << std::endl; }

    template<class C, class... Args>
    DelayedSignal ( void(C::*func)(Args...), C& obj, Args... args )
    {
        std::cout << "DelayedSignal INIT - 04 - pointer to method & pointer to class instance & arguments (Arg num: " << sizeof...(Args) << ")" << std::endl;
    }
};

template<class... ArgsBis>
struct DelayedSignal_DebugHelper 
{
    ~DelayedSignal_DebugHelper ()
    { std::cout << "~DelayedSignal_DebugHelper CLOSE" << std::endl; }

    template<class C, class... Args>
    DelayedSignal_DebugHelper ( void(C::*func)(Args...), C& obj )
    { std::cout << "DelayedSignal_DebugHelper INIT - 03 - pointer to method & pointer to class instance (Arg num: " << sizeof...(Args) << ")" << std::endl; }

    template<class C, class... Args>
    DelayedSignal_DebugHelper ( void(C::*func)(Args...), C& obj, ArgsBis... args ) // Need to use ArgsBis instead of Args to make it work
    {
        std::cout << "DelayedSignal_DebugHelper INIT - 04 - pointer to method & pointer to class instance & arguments (Arg num: " << sizeof...(Args) << ")" << std::endl;
    }
};


template < class Tr, class... Args >
struct Signal
{
    void fire ( Args... args ) { std::cout << "Signal::fire::" << sizeof...(Args) << std::endl; }
};

struct Klass {};


int main()
{
    std::string str1("Blop");   // Will be used as reference
    Klass k;                    // Will be used as reference

    Signal<void, Klass&> signal_01;
    Signal<void, std::string&> signal_02;

    std::cout << "====== DelayedSignal :: needed for production purpose ===============" << std::endl;

    // OK
    DelayedSignal test01(&Signal<void, std::string&>::fire, signal_02);
    // HERE IS THE PROBLEM
    //DelayedSignal test02(&Signal<void, std::string&>::fire, signal_02, str1);

    // OK
    DelayedSignal test03(&Signal<void, Klass&>::fire, signal_01);
    // HERE IS THE PROBLEM
    //DelayedSignal test04(&Signal<void, Klass&>::fire, signal_01, k);

    std::cout << "====== DelayedSignal_DebugHelper :: used only for debug purpose ======" << std::endl;

    // OK
    DelayedSignal_DebugHelper<std::string&> test05(&Signal<void, std::string&>::fire, signal_02);
    // OK
    DelayedSignal_DebugHelper<std::string&> test06(&Signal<void, std::string&>::fire, signal_02, str1);

    // OK
    DelayedSignal_DebugHelper<Klass&> test07(&Signal<void, Klass&>::fire, signal_01);
    // OK
    DelayedSignal_DebugHelper<Klass&> test08(&Signal<void, Klass&>::fire, signal_01, k);

    return 1;
}

As I register all DelayedSignal instances into a single std::list instance I would like to avoid using template on the class itself, and that's why I use templates on the constructors instead. I could also use a pure virtual class as base for all DelayedSignal and register pointers to the virtual class into the std::list but I think it's best to minimize the use of virtual methods and I'm really intrigued by this problem...

As you can see in this example, test02 and test04 return errors if they are activated. DelayedSignal_DebugHelper is almost identical to DelayedSignal except the fact that it use ArgsBis (a class template argument) on the last constructor instead of the Args template (the method template argument), else it doesn't work (as with DelayedSignal). Args is accepted on the void(C::*func)(Args...) but is not with ArgsBis... args dispite the fact they are in the same constructor declaration.

As far as I know, there is no problem without references (DelayedSignal test04(&Signal<void, Klass>::fire, signal_01, k); for instance) or with multiple parameters (or none) as long as there is no references.

Is there anyway to fix this problem ?

Thank you.

like image 965
Valkea Avatar asked Apr 04 '11 04:04

Valkea


People also ask

What is Variadic template in C++?

A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.

Which of the following are valid reasons for using variadic templates in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.

How do you use Variadic arguments?

It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.

Is printf variadic function?

The C printf() function is implemented as a variadic function. This noncompliant code example swaps its null-terminated byte string and integer parameters with respect to how they are specified in the format string.


2 Answers

I'm using clang which gives an absolutely fabulous error message:

test.cpp:59:19: error: no matching constructor for initialization of 'DelayedSignal'
    DelayedSignal test02(&Signal<void, std::string&>::fire, signal_02, str1);
                  ^      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:9:5: note: candidate constructor template not viable: requires 2 arguments, but 3 were provided
    DelayedSignal ( void(C::*func)(Args...), C& obj )
    ^
test.cpp:13:5: note: candidate template ignored: deduced conflicting types for parameter 'Args'
      (<std::__1::basic_string<char> &> vs. <std::__1::basic_string<char>>)
    DelayedSignal ( void(C::*func)(Args...), C& obj, Args... args )
    ^

The compiler deduces conflicting types for Args:

  1. std::string&
  2. std::string

I believe the best way to fix this is exactly how you have done so with your DelayedSignal_DebugHelper.

like image 64
Howard Hinnant Avatar answered Nov 15 '22 18:11

Howard Hinnant


Howard Hinnant is right... The other possibility you have is to use references everywhere, e.g.:

#include <iostream>

struct DelayedSignal 
{   
    ~DelayedSignal ()
     { std::cout << "~DelayedSignal CLOSE" << std::endl; }

    template<class C, class... Args>
    DelayedSignal ( void(C::*func)(Args &...), C& obj )
    { std::cout << "DelayedSignal INIT - 03 - pointer to method & pointer to class instance (Arg num: " << sizeof...(Args) << ")" << std::endl; }

    template<class C, class... Args>
    DelayedSignal ( void(C::*func)(Args &...), C& obj, Args & ... args )
    {
        std::cout << "DelayedSignal INIT - 04 - pointer to method & pointer to class instance & arguments (Arg num: " << sizeof...(Args) << ")" << std::endl;
    }
};

template < class Tr, class... Args >
struct Signal
{
     void fire ( Args &... args ) { std::cout << "Signal::fire::" << sizeof...(Args) << std::endl; }
};

struct Klass {};

int main()
{
    std::string str1("Blop");   // Will be used as reference
    Klass k;                    // Will be used as reference

    Signal<void, Klass&> signal_01;
    Signal<void, std::string&> signal_02;

    std::cout << "====== DelayedSignal :: needed for production purpose ===============" << std::endl;

    // OK
    DelayedSignal test01(&Signal<void, std::string&>::fire, signal_02);
    // HERE IS THE PROBLEM
    DelayedSignal test02(&Signal<void, std::string&>::fire, signal_02, str1);

}
like image 39
bluescarni Avatar answered Nov 15 '22 20:11

bluescarni