5

I have a working virtual function add which uses following setup:

using Func = std::function<std::vector<unsigned char>()>;      

class cfExecutor {
public:
    cfExecutor();
    virtual ~cfExecutor();
    /// Enqueue a function to be executed by this executor. This and all                     
    /// variants must be threadsafe.
    virtual void add(Func) = 0;                              
    virtual void add(std::vector<Variant> params, Func callback) = 0; 
};


class ManualExecutor : public cfExecutor                                                                            
{                                                                                                                   

    std::mutex lock_;             // avoid multiple entity updating the function container                          
    std::queue<Func> funcs_;      // functions queued for running                                                 

    public:                                                                                                         
    std::map<int8_t, Func> funcs; // Function container (priority,Func) - added functions to this Executors       

    ManualExecutor() {}                                                                                             
    ManualExecutor(ManualExecutor&& other):                                                                         
                    funcs(std::move(other.funcs))                                                                   
    {}                                                                                                              
    ~ManualExecutor() {}                                                                                            

    void add(Func callback);                                                                          
    void add(std::vector<Variant> params, Func callback);                     
};                                                                                                                  

I then wanted to add variadic params to the function - like this :

using Func = std::function<std::vector<unsigned char>(const auto&...args)>;  

However I get implicit error [implicit templates may not be ‘virtual’]

How should I define the add function with the variadic params ??

so far I have solved it using following:

using Func = std::function<std::vector<unsigned char>(std::vector<Variant>)>;

and then having the lambda function handle the receiving args from inside a vector - something like this:

auto lambdaFunction = [](std::vector<Variant> vec){        

    std::vector<unsigned char> result;                     
    cout << "- Hello from executor - lambdaFunction ";     
    std::cout << ": parameters : ";                        
    for(auto a: vec) {                                     
        std::cout << a << ",";                             
    }                                                      
    std::cout << std::endl;                                 

    std::string s("this is the result");                   
    result.insert(result.end(),s.begin(), s.end());        
    return result;                                         
};        
serup
  • 3,676
  • 2
  • 30
  • 34
  • 1
    When you use `auto ... ` then `Func` is not concrete type. You need to make it a template alias, as: `template using Func = std::function;` – Nawaz Jan 30 '17 at 10:16
  • 3
    Do you want to add functions with different signatures to the same cfExecuror, or functions with the same variadic signature, or something else? – n. m. could be an AI Jan 30 '17 at 10:27
  • different signatures would be nice, however sofar I just want to have functions which takes variadic params – serup Jan 30 '17 at 10:39
  • @serup: What is your objective and your goal? What do you want to do with this? I feel if you approach your goal in a different way, you probably would not even have to write this class with `virtual` functions. – Nawaz Jan 30 '17 at 10:42
  • @Nawaz, I have several different executors and one is called ManualExecutor - it works fine, however now I would like to add parameters to the functions that I add to the executors - the executors are being executed in promises/futures – serup Jan 30 '17 at 11:12
  • "different signatures would be nice" How would you call them? "functions which takes variadic params" There are no such functions in C++ unless you count ones that take the old varargs-style parameters, but even if there were, how would you call them? – n. m. could be an AI Jan 30 '17 at 18:14
  • well since the (const Args&...args) parameter setup for functions somehow does not work for lambdas, since pt. lambda functions can not have template, then I do not know - however what is the use of a queue full of lambda functions with no way of passing arguments? – serup Jan 31 '17 at 07:46
  • Forget C++ rules. Assume they will let you bend types however you want. You have a bunch of functions with unknown number and types of arguments. Why do you need them? How would you use them? – n. m. could be an AI Jan 31 '17 at 08:09
  • @n.m. picture this - you have a queue of lambda functions, this queue you can fetch the lambda's from and execute them one by one or spawn each in a new thread -- what I want is to do this with parameters - sofar I have this working for parameterless lambdas – serup Jan 31 '17 at 08:13
  • In order to call a function, a lambda or anything else you need to know what parameters to pass to it. If you are know there are no parameters you are set. What happens with functions you don't know the parameters of? – n. m. could be an AI Jan 31 '17 at 08:21
  • @n.m. those parameters are called variadic params and are usually used in lambdas like this: `[](const auto& ...args) { }` however this can not be used when lambda is added to a queue with another functions - why I do not know, hence this post – serup Jan 31 '17 at 08:41
  • It looks like we're not on the same page (more like in different libraries). Can you show what you want to do in pseudocode, ignoring types but showing every single variable? – n. m. could be an AI Jan 31 '17 at 09:01
  • let me try: `add_function_to_queue(fn1(a, b, c)) add_function_to_queue(fn2(a, b, c)) add_function_to_queue(fn3(a, b, c)) ... foreach function in queue { run(function(ref a,ref b,ref c) }` – serup Jan 31 '17 at 09:03
  • Ok we're close but what are `a b c`? Shouldn't they be different for each function? Where does the component that executes the queue get different `a b c` for each function? – n. m. could be an AI Feb 02 '17 at 08:40
  • @n.m. it seems that what I was trying is not really possible due to the compiler / runtime scope - the parameter packs are on compile time, thus not possible to use on a runtime virtual function, hence the need for a fixed parameter setup, and I chose `std::vector` and then modifying my lambdas to cope with this - it is not really what I wanted, however due to the compiler/runtime constraints, then this is it pt. – serup Feb 02 '17 at 08:49
  • We are losing each other again. I have asked you a question about what you *want* your `a, b, c` to be, not about any possible C++ implementation. Can you answer the question without saying words like "virtual" or "template" or "parameter pack" or "variant" or "vector"? – n. m. could be an AI Feb 02 '17 at 08:53
  • At the very least can you show your *entire* implementation of an executor using `vector` and a call to `add` as they are in your real code? – n. m. could be an AI Feb 02 '17 at 08:59
  • @n.m this is how I do it : `void addexecutorfunc( Func callback, const auto&...args ) { std::vector vec = {args...}; executor.add(vec, std::move(callback)); }` – serup Feb 02 '17 at 09:12
  • @n.m the actual code is too large to post here – serup Feb 02 '17 at 09:21
  • OK, something that compiles? – n. m. could be an AI Feb 02 '17 at 09:38
  • what I have shown sofar compiles – serup Feb 02 '17 at 09:41
  • OK so you want `virtual void add(std::vector params, Func callback) = 0;`. I should have guessed so. It doesn't make much sense for lambdas because you can capture additional data in the lambda itself. I'll write a short answer in a moment. – n. m. could be an AI Feb 02 '17 at 11:43

2 Answers2

2

AFAIK, you cannot use 'auto' in function prototype yet.

You probably want to do like following:

template<typename ... Args >
using Func =std::function<std::vector<unsigned char>(const Args&...args)>;

template<typename ... Args >
class cfExecutor {                        
 public:   
     cfExecutor();                        
    virtual ~cfExecutor(); 
     virtual void add(Func<Args...>) = 0;

    };
P0W
  • 46,614
  • 9
  • 72
  • 119
  • Note that `args` is not needed in the template alias. – Nawaz Jan 30 '17 at 10:23
  • I'm having problem using this principle in a class that has this cfExecutor as parent – serup Jan 30 '17 at 10:35
  • I get invalid use of template-name - not really sure what is happening with this setup, perhaps I do not really understand your solution - I added more info to my question, could you take a look again – serup Jan 30 '17 at 10:55
  • @serup not sure how are you passing lambda, and it might need a separate post on its own – P0W Jan 31 '17 at 07:54
  • @P0W, yes I agree perhaps a different post - somehow the issue of forwarding arguments for lambda functions stored on a queue is not so trivial, it is however easy to have lambda functions on a queue for later execution, however the whole thing about argument/parameter transfer is hidden in some template setup, which pt. is not possible for lambdas to use - I think – serup Jan 31 '17 at 08:02
  • @POW, perhaps make a new post and build on this example : http://stackoverflow.com/a/22109991/3990012 what do you think - how to add parameters to the lambdas and how to forward the parameters to lambdas being executed – serup Jan 31 '17 at 08:22
  • @serup Sorry I didn't get the question. You are allowed to answer your own post if you get some solution on your own. – P0W Jan 31 '17 at 08:24
  • you suggested a new post for passing lambdas - I highlighted the current issues – serup Jan 31 '17 at 08:31
  • This post is about the parameter transfer to lambdas inside a queue, and so far normal template setup is not possible, hence this post - then how to do it ??? – serup Jan 31 '17 at 08:32
2

It looks like you want to pass a bound function.

This can be done with std::bind. You don't need to change add in any way or add a vector<variant>-based overload. Just call it as follows:

std::vector<unsigned char> myfunction (int, const char*, Foo&);

...
int i = 123;
const char* s = "abc";
Foo foo{1,2,3};
executor->add(std::bind(myfunction, i, s, foo));

Use it with regular functions, member functions, function-like objects ("functors") or lambdas. With inline lambdas it doesn't make much sense though as you can capture the data in the lambda itself.

executor->add([&i,s,&foo](){ ... use(i); use(s); use(foo); }

In fact you can always replace a bind expression with a lambda which can me more readable. Compare:

int foo;
std::bind(std::operator<<, std::cout, foo);

vs

[foo]() { return std::cout << foo; }
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243