Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to have a pointer to a function with arbitrary arguments as a template parameter?
    primarykey
    data
    text
    <p>This is a semantic-optimization problem I've been working on over the past couple days, and I'm stuck. My real program runs on a RTOS (FreeRTOS, specifically), and I need to spawn tasks (which are simplified, non-terminating versions of threads). The C API takes a <code>void (*)(void*)</code> for the task's entry point, and a <code>void*</code> parameter. Pretty standard fare.</p> <p>I've written a wrapper class for a task, and rather than doing one of the old-school implementations such as having a virtual method that must be overridden by the final task class, I would rather get C++ to generate the necessary parameter-storage object and glue functions by means of variadic templates and functions.</p> <p>I've done this with lambdas and <code>std::function</code> and <code>std::bind</code> already, but they seem to implement some bloat, namely by not resolving the function target until runtime. Basically the same mechanism as a virtual method would use. I'm trying to cut out all the overhead I can, if possible. The bloat has been coming out to about 200 bytes per instance more than the hard-coded implementation. (This is on an ARM Cortex-M3 with 128K total flash, and we've only got about 500 bytes left.) All of the SO questions I've found on the topic similarly defer resolution of the function until runtime.</p> <p>The idea is for the code to:</p> <ol> <li>Store the decayed versions of the variadic arguments in an object allocated on the heap (this is a simplification; an Allocator could be used instead), and pass this as the <code>void*</code> parameter,</li> <li>Pass a generated call-island function as the entry point, with signature <code>void(void*)</code>, that calls the target function with the stored parameters, and</li> <li>(This is the part I can't figure out) have the compiler deduce the types of the argument list from the target function's signature, to follow the <em>Don't Repeat Yourself</em> principle.</li> <li>Note that <strong>the function pointer and its argument types are known and resolved at compile time,</strong> and the actual <strong>argument values passed to the function are not known until runtime</strong> (because they include things like object pointers and runtime configuration options).</li> </ol> <p>In the example below, I have to instantiate one of the tasks as <code>Task&lt;void (*)(int), bar, int&gt; task_bar(100);</code> when <strong>I would rather write <code>Task&lt;bar&gt; task_bar(100);</code> or <code>Task task_bar&lt;bar&gt;(100);</code></strong> and have the compiler figure out (or somehow tell it in the library) that the variadic arguments have to match the argument list of the specified function.</p> <p>The "obvious" answer would be some kind of template signature like <code>template&lt;typename... Args, void (*Function)(Args...)&gt;</code> but, needless to say, that does not compile. Nor does the case where <code>Function</code> is the first argument.</p> <p>I'm not sure this is even possible, so I'm asking here to see what you guys come up with. I've omitted the variant code that targets object methods instead of static functions in order to simplify the question.</p> <p>The following is a representative test case. I'm building it with gcc 4.7.3 and the <code>-std=gnu++11</code> flag.</p> <pre><code>#include &lt;utility&gt; #include &lt;iostream&gt; using namespace std; void foo() { cout &lt;&lt; "foo()\n"; } void bar(int val) { cout &lt;&lt; "bar(" &lt;&lt; val &lt;&lt; ")\n"; } template&lt;typename Callable, Callable Target, typename... Args&gt; struct TaskArgs; template&lt;typename Callable, Callable Target&gt; struct TaskArgs&lt;Callable, Target&gt; { constexpr TaskArgs() {} template&lt;typename... Args&gt; void CallFunction(Args&amp;&amp;... args) const { Target(std::forward&lt;Args&gt;(args)...); } }; template&lt;typename Callable, Callable Target, typename ThisArg, typename... Args&gt; struct TaskArgs&lt;Callable, Target, ThisArg, Args...&gt; { typename std::decay&lt;ThisArg&gt;::type arg; TaskArgs&lt;Callable, Target, Args...&gt; sub; constexpr TaskArgs(ThisArg&amp;&amp; arg_, Args&amp;&amp;... remain) : arg(arg_), sub(std::forward&lt;Args&gt;(remain)...) {} template&lt;typename... CurrentArgs&gt; void CallFunction(CurrentArgs&amp;&amp;... args) const { sub.CallFunction(std::forward&lt;CurrentArgs&gt;(args)..., arg); } }; template&lt;typename Callable, Callable Target, typename... Args&gt; struct TaskFunction { TaskArgs&lt;Callable, Target, Args...&gt; args; constexpr TaskFunction(Args&amp;&amp;... args_) : args(std::forward&lt;Args&gt;(args_)...) {} void operator()() const { args.CallFunction(); } }; // Would really rather template the constructor instead of the whole class. // Nothing else in the class is virtual, either. template&lt;typename Callable, Callable Entry, typename... Args&gt; class Task { public: typedef TaskFunction&lt;Callable, Entry, Args...&gt; Function; Task(Args&amp;&amp;... args): taskEntryPoint(&amp;Exec&lt;Function&gt;), taskParam(new Function(std::forward&lt;Args&gt;(args)...)) { Run(); } template&lt;typename Target&gt; static void Exec(void* param) { (*static_cast&lt;Target*&gt;(param))(); } // RTOS actually calls something like Run() from within the new task. void Run() { (*taskEntryPoint)(taskParam); } private: // RTOS actually stores these. void (*taskEntryPoint)(void*); void* taskParam; }; int main() { Task&lt;void (*)(), foo&gt; task_foo; Task&lt;void (*)(int), bar, int&gt; task_bar(100); return 0; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload