1
class EG
{
template<class T>
std::function<void(T)> f;

//these are supposed to be appropriate instantiations that I have to look up how to do as I haven't really used templates
template<std::string> f;
template<int> f;

template<class T>
void func(std::function<void(T)>&& arg)
{
f<std::string> = nullptr;
f<int> = nullptr;
f.swap(arg);
}

};

Here is what I will do.

EG eg;
eg.func([](std::string s){});
eg.func([](int i){});

There is a callback cache (f) and it can take different arguments (pointer to object, a location vector and such). It is used for mouse targeting in a simulation program. When I start another targeting progress, I need to cancel the current target (which means f = null for now). I just don't want a lot of functions and members that actually do the same thing (but for different arguments). How can I solve this problem?

I can simply check if there is a target with a bool instead of f == null but I still need multiple f instances for different callback arguments). Using a bool instead of f == nullptr check will prevent me from having a templated class. Checking if f == nullptr also prevent me from having a templated class.

More information about the case:

This is actually a callback handler and T only has two different types in use (currently).

I am trying to prevent having multiple f1, f2... and void func variations for them.

Etherealone
  • 3,488
  • 2
  • 37
  • 56
  • "My feeling is" Why don't you just put a `cout` in `func` and see? – Matt Phillips May 15 '13 at 17:55
  • The new C++ features and template functionality hurts my head, but can you not instantiate the types you want specifically? – im so confused May 15 '13 at 17:57
  • 3
    `f` is a function that expects an argument of type `T`, and you're calling it with no arguments? – dyp May 15 '13 at 17:58
  • @AK4749 I am currently doing it but there were cases that required more variations and not being able to do this hold me back, did not want it more complicated. – Etherealone May 15 '13 at 18:00
  • 1
    Besides, I think the first `template` belongs *above* `class EG`. – dyp May 15 '13 at 18:00
  • 2
    This code doesn't come close to compiling, maybe re-work the example. http://ideone.com/ifmyUU – Matt Phillips May 15 '13 at 18:01
  • You can have template member variables in C++11? – Mark B May 15 '13 at 18:01
  • @MarkB No, and I'm pretty sure they're still not allowed in C++1y. – dyp May 15 '13 at 18:03
  • @MattPhillips okay I am trying to build a real case now, will edit. – Etherealone May 15 '13 at 18:03
  • @DyP http://stackoverflow.com/questions/972152/how-to-create-a-template-function-within-a-class-c – Etherealone May 15 '13 at 18:04
  • @DyP we can define methods but not members? – Etherealone May 15 '13 at 18:06
  • @Tolga But `template std::function f;` is not a member function. just `std::function f;` would be a data member ("member variable") if there was some definition of type `T`; with the template it's ..... illegal (not valid C++) – dyp May 15 '13 at 18:06
  • @Tolga What do you want to do? – dyp May 15 '13 at 18:06
  • @DyP there is a callback cache (f) and it can take different arguments (pointer to object, a location vector and such). It is used for mouse targeting in a simulation program. When I start another targeting progress, I need to cancel the current target (which means f = null for now). I just don't want a lot of functions and members that actually do the same thing. I can simply check if there is a target with a bool instead of f == null but I still need multiple f instances for different callback arguments) – Etherealone May 15 '13 at 18:09
  • I have edited the question as I learned there are no template members, but only methods. Still I am looking for how to emulate this at least. – Etherealone May 15 '13 at 18:14
  • Can anyone enlighten me on why this would ever be useful other than for obfuscation? – rubenvb May 15 '13 at 18:57

1 Answers1

1

This is a bit long for a comment, so I'll post this as an answer:

class EG
{
    template<class T>
    std::function<void(T)> f;  // a data member template?

    template<class T>
    void func(std::function<void(T)>&& arg)
    {
        f = nullptr;
        f.swap(arg);
        // could as well just
        // f = std::move(arg);
    }
};

If your use case is:

EG obj;

obj.func( [](int i) { std::cout << i; } );
obj.f(42);

obj.func( [](string s) { std::cout << s; } );
obj.f("hello world");

Than this is highly error-prone, as the compiler cannot check the types of the argument of the function call obj.f as the argument types are supposed to be variable: If you assign a void(int) via obj.func, than it shall accept an int; if you assign a void(string) than it shall accept a string.

There are no data member templates in C++ (IMO doesn't make sense to have them - when should they be instantiated?), but it is possible to get the behaviour of the use case above.


A simpler, less error-prone solution:

#include <functional>
#include <string>
#include <iostream>
#include <memory>

using std::string;

struct any_function
{
    virtual ~any_function()
    {}
};
template < typename T >
struct concrete_function
    : public any_function
{
    T m;
    concrete_function(T&& p)
        : m( std::move(p) )
    {}
    virtual ~concrete_function() override
    {}
};

struct EG
{
private:
    std::shared_ptr<any_function> fs;

public:
    EG& operator=(std::function<void(int)>&& p) // do NOT templatize this!!!!
    {
        using stor_type = concrete_function< std::function<void(int)> >;
        fs = std::make_shared<stor_type>( std::move(p) );
        return *this;
    }
    EG& operator=(std::function<void(string)>&& p) // do NOT templatize this!!!!
    {
        using stor_type = concrete_function< std::function<void(string)> >;
        fs = std::make_shared<stor_type>( std::move(p) );
        return *this;
    }

    void operator()(int p) // do NOT templatize this!!!!
    {
        using stor_type = concrete_function< std::function<void(int)> >;
        auto const& ptr = std::static_pointer_cast< stor_type >(fs);
        if(ptr) {  ptr->m(p);  }
        else { /* throw something */ }
    }
    void operator()(string p) // do NOT templatize this!!!!
    {
        using stor_type = concrete_function< std::function<void(string)> >;
        auto const& ptr = std::static_pointer_cast< stor_type >(fs);
        if(ptr) {  ptr->m(p);  }
        else { /* throw something */ }
    }
};


int main()
{
    EG obj;

    obj = [](int i) { std::cout << i; };
    obj(42);       // using overload mechanism
}

You shouldn't templatize the marked functions as the type used in the creation (make_shared) has to match exactly the type used in the call (static_pointer_cast).

Or, using enums:

#include <functional>
#include <string>
#include <iostream>

using std::string;

struct EG
{
    enum stored_function_type
    {
        INT_FUNC, STRING_FUNC
    };

    union function_storage
    {
        std::function<void(int)> f_int;
        std::function<void(string)> f_string;

        function_storage()
             : f_int()
        {}
        ~function_storage()
        {}
    };

    stored_function_type ft;
    function_storage fs;

    template < typename T >  void destroy(T& p)  {  p.~T();  }
    void destroy()
    {
        switch(ft)
        {
        case INT_FUNC:
            destroy(fs.f_int);
        break;
        case STRING_FUNC:
            destroy(fs.f_string);
        break;
        }
    }

    EG()
      : ft(INT_FUNC)
    {}
    ~EG()
    {
        destroy();
    }

    EG& operator=(std::function<void(int)>&& p)
    {
        destroy();
        new(&fs.f_int) std::function<void(int)>( std::move(p) );
        ft = INT_FUNC;

        return *this;
    }
    EG& operator=(std::function<void(string)>&& p)
    {
        destroy();
        new(&fs.f_string) std::function<void(string)>( std::move(p) );
        ft = STRING_FUNC;

        return *this;
    }

    void operator()(int p)
    {
        // better check ft == INT_FUNC
        return fs.f_int(p);
    }
    void operator()(string p)
    {
        // better check ft == STRING_FUNC
        return fs.f_string(p);
    }
};


int main()
{
    EG obj;

    obj = [](int i) { std::cout << i; };
    obj(42);       // using overload mechanism
}

You could simplify this for many types using fancy template metaprogamming.

Disclaimer: It's a long time since I've used unions.... might miss something.

dyp
  • 38,334
  • 13
  • 112
  • 177
  • This was my first approach to it. I am trying to find a way to accessing multiple std::functions with same variable name. Now templated member idea also look pointless to me. Using another overload to access a callback didn't come to my mind, it looks like it will simplify things some at least. – Etherealone May 15 '13 at 18:35
  • If there is no way to use same variable name f (templated) and set it in one templated func method, I am marking this as answered. – Etherealone May 15 '13 at 18:37
  • @Tolga You could use dynamic memory allocation combined with polymorphism. I'll try to make an example. – dyp May 15 '13 at 18:52
  • That seems to complicate more for the moment. It will be useful if number of types go over 5 though. Thank you. – Etherealone May 15 '13 at 19:09
  • @Tolga A slightly better version using polymorphism. See edit (middle of answer). – dyp May 15 '13 at 19:13