You can use std :: function to store a function with variable arguments

I have a structure that I pass to my application that contains a bunch of callback functions:

typedef struct { std::function<void (void)> f1; std::function<void (int)> f2; std::function<int (float *)> f3; // ... and so on } CallbackTable; 

I handle state control inside the application, associating various functions with various callbacks, depending on the current state of the system; It works great.

Now I would like to add some additional callbacks with signatures containing variable numbers of arguments, akin to printf: for example,

  std::function<int (const char * format, ...)> printToStdOut; 

This does not work. In Visual C ++, I get an error:

 error C2027: use of undefined type 'std::_Get_function_impl<_Fty>' 

I still feel my way through this area of โ€‹โ€‹C ++ syntax and really appreciate any advice on how I should proceed from here. My main goal is to make a line-by-line call:

 myCallbackTable.printToStdOut("I've just eaten %d bananas\r\n", nBananas); 

... and have the output directed to the console to a file or to a window of a graphical user interface in accordance with the state of the system.

+6
source share
2 answers

Edit: the original answer was incorrect, modified, but still may not be the best answer, leaving it here for educational purposes:

Many variable argument functions have a va_list version. printf for example has vprintf . These options explicitly accept va_list instead of ellipses.

 #include <iostream> #include <functional> #include <cstdarg> int main() { std::function<int(const char*,va_list)> test2(vprintf); return 0; } 

Calling him, however, is a pain.

 int invoke_variadic(const std::function<int(const char*,va_list)>& ref,const char* a ,...) { va_list args; va_start(args,a); ref(a,args); va_end(args); } 

What happened to the original post (thanks / u / TC): std::function<int(const char*,va_list)> test2(printf) compilers, which I understood to "work". However, it compiles because printf can accept arguments of any type (including va_list), and std::function checks if it can be called that way.

+3
source

Following what @MadScienceDreams suggested, this worked out fine for me ...

First, I defined the argument variables of the versions of std :: function objects in the structure, and then used the fact that this is C ++ not C to add some methods:

 typedef struct { std::function<void (void)> f1; std::function<void (int)> f2; std::function<int (float *)> f3; // ... and so on std::function<int (const char * format, va_list args)> vprintToStdOut; std::function<int (const char * format, va_list args)> vprintToStdErr; int printToStdOut(const char * format, ...) { va_list ap; va_start(ap, format); int count = vprintToStdOut(format, ap); va_end(ap); return count; } int printToStdErr(const char * format, ...) { va_list ap; va_start(ap, format); int count = vprintToStdErr(format, ap); va_end(ap); return count; } } CallbackTable; 

The standard implementations of the function of the argument variables for output to the console were then (after the declaration "using namespace std :: placeholders;", without which the syntax is uncontrollable):

 myCallbackTable.vprintToStdOut = std::bind(vfprintf, stdout, _1, _2); myCallbackTable.vprintToStdErr = std::bind(vfprintf, stderr, _1, _2); 

... and from this point of view, I can use exactly the syntax that I was hoping to use:

 myCallbackTable.printToStdOut("I like to eat at least %d bananas a day\r\n", nBananas); 
0
source

Source: https://habr.com/ru/post/974843/


All Articles