Okay, I have posted a few questions lately related to wrapping a C callback API with a C++11-ish interface. I have almost got a satisfying solution, but I think it could be more elegant and need the help of some template metaprogramming wizards :)
Bear with me, as the example code is a little long, but I've tried to demonstrate the problem in one shot. Basically, the idea is that, given a list of function pointers and data context pointers, I want to provide a callback mechanism that can be provided with,
Function pointers
Function objects (functors)
Lambdas
Moreover, I want to make these functions callable by a variety of prototypes. What I mean is, the C API provides about 7 different parameters to the callback, but in most cases the user code is really only interested in one or two of these. So I'd like the user to be able to specify only the arguments he is interested in. (This extends from the point of allowing lambdas in the first place... to allow conciseness.)
In this example, the nominal C callback takes an int and a float parameter, and an optional float* which can be used to return some extra data. So the intention of the C++ code is to be able to provide a callback of any of these prototypes, in any form that is "callable". (e.g. functor, lambda, etc.)
int callback2args(int a, float b);
int callback3args(int a, float b, float *c);
Here is my solution so far.
#include <cstdio>
#include <vector>
#include <functional>
typedef int call2args(int,float);
typedef int call3args(int,float,float*);
typedef std::function<call2args> fcall2args;
typedef std::function<call3args> fcall3args;
typedef int callback(int,float,float*,void*);
typedef std::pair<callback*,void*> cb;
std::vector<cb> callbacks;
template <typename H>
static
int call(int a, float b, float *c, void *user);
template <>
int call<call2args>(int a, float b, float *c, void *user)
{
call2args *h = (call2args*)user;
return (*h)(a, b);
}
template <>
int call<call3args>(int a, float b, float *c, void *user)
{
call3args *h = (call3args*)user;
return (*h)(a, b, c);
}
template <>
int call<fcall2args>(int a, float b, float *c, void *user)
{
fcall2args *h = (fcall2args*)user;
return (*h)(a, b);
}
template <>
int call<fcall3args>(int a, float b, float *c, void *user)
{
fcall3args *h = (fcall3args*)user;
return (*h)(a, b, c);
}
template<typename H>
void add_callback(const H &h)
{
H *j = new H(h);
callbacks.push_back(cb(call<H>, (void*)j));
}
template<>
void add_callback<call2args>(const call2args &h)
{
callbacks.push_back(cb(call<call2args>, (void*)h));
}
template<>
void add_callback<call3args>(const call3args &h)
{
callbacks.push_back(cb(call<call3args>, (void*)h));
}
template<>
void add_callback<fcall2args>(const fcall2args &h)
{
fcall2args *j = new fcall2args(h);
callbacks.push_back(cb(call<fcall2args>, (void*)j));
}
template<>
void add_callback<fcall3args>(const fcall3args &h)
{
fcall3args *j = new fcall3args(h);
callbacks.push_back(cb(call<fcall3args>, (void*)j));
}
// Regular C-style callback functions (context-free)
int test1(int a, float b)
{
printf("test1 -- a: %d, b: %f", a, b);
return a*b;
}
int test2(int a, float b, float *c)
{
printf("test2 -- a: %d, b: %f", a, b);
*c = a*b;
return a*b;
}
void init()
{
// A functor class
class test3
{
public:
test3(int j) : _j(j) {};
int operator () (int a, float b)
{
printf("test3 -- a: %d, b: %f", a, b);
return a*b*_j;
}
private:
int _j;
};
// Regular function pointer of 2 parameters
add_callback(test1);
// Regular function pointer of 3 parameters
add_callback(test2);
// Some lambda context!
int j = 5;
// Wrap a 2-parameter functor in std::function
add_callback(fcall2args(test3(j)));
// Wrap a 2-parameter lambda in std::function
add_callback(fcall2args([j](int a, float b)
{
printf("test4 -- a: %d, b: %f", a, b);
return a*b*j;
}));
// Wrap a 3-parameter lambda in std::function
add_callback(fcall3args([j](int a, float b, float *c)
{
printf("test5 -- a: %d, b: %f", a, b);
*c = a*b*j;
return a*b*j;
}));
}
int main()
{
init();
auto c = callbacks.begin();
while (c!=callbacks.end()) {
float d=0;
int r = c->first(2,3,&d,c->second);
printf(" result: %d (%f)\n", r, d);
c ++;
}
}
Okay, as you can see, this actually works. However, I find the solution of having to explicitly wrap the functors/lambdas as std::function types kind of inelegant. I really wanted to make the compiler match the function type automatically but this doesn't seem to work. If I remove the 3-parameter variant, then the fcall2args wrapper is not needed, however the presence of the fcall3args version of add_callback makes it apparently ambiguous to the compiler. In other words it seems to not be able to do pattern matching based on the lambda call signature.
A second problem is that I'm of course making copies of the functor/lambda objects using new, but not deleteing this memory. I'm not at the moment sure what the best way will be to track these allocations, although I guess in a real implementation I could track them in an object of which add_callback is a member, and free them in the destructor.
Thirdly, I don't find it very elegant to have specific types call2args, call3args, etc., for each variation of the callback I want to allow. It means I'll need an explosion of types for every combination of parameters the user might need. I was hoping there could be some template solution to make this more generic, but I am having trouble coming up with it.
Edit for explanation: The definition in this code, std::vector<std::pair<callback*,void*>> callbacks, is part of the problem definition, not part of the answer. The problem I am trying to solve is to map C++ objects onto this interface--therefore, proposing better ways to organize this std::vector doesn't solve the problem for me. Thanks. Just to clarify.
Edit #2: Okay, forget the fact that my example code uses std::vector<std::pair<callback*,void*>> callbacks to hold the callbacks. Imagine instead, as this is the actual scenario, that I have some C library implementing the following interface:
struct someobject *create_object();
free_object(struct someobject *obj);
add_object_callback(struct someobject *obj, callback *c, void *context);
where callback is,
typedef int callback(int a,float b,float *c, void *context);
Okay. So "someobject" will experience external events of some kind, network data, or input events, etc., and call its list of callbacks when these happen.
This is a pretty standard implementation of callbacks in C. Importantly, this is an existing library, something for which I cannot change, but I am trying to write a nice, idiomatic C++ wrapper around it. I want my C++ users to be able to add lambdas as callbacks. So, I want to design a C++ interface that allows users to be able to do the following:
add_object_callback(struct someobject *obj, func);
where func is one of the following:
a regular C function that doesn't use context.
a functor object
a lambda
Additionally, in each case, it should be possible for the function/functor/lambda to have either of the following signatures:
int cb2args(int a, float b);
int cb2args(int a, float b, float *c);
I think this should be possible, and I got about 80% of the way there, but I'm stuck on template polymorphism based on the call signature. I don't know offhand whether it's possible. Maybe it needs some voodoo involving function_traits or something, but it's a little beyond my experience. In any case, there are many, many C libraries that use such an interface, and I think it would be great to allow this kind of convenience when using them from C++.
Since you are using the C API in C++11, you could as well just wrap the whole thing in a C++ class. This is also necessary, as you mentioned in the 2nd problem, to solve the resource leak.
Also remember that a lambda expression without capture can be implicitly converted to a function pointer. This could remove all the call<*> because they can be moved into the add_callbacks.
And finally, we could use SFINAE to remove the fcall3args types. Here is the result.
class SomeObject {
// The real object being wrapped.
struct someobject* m_self;
// The vector of callbacks which requires destruction. This vector is only a
// memory store, and serves no purpose otherwise.
typedef std::function<int(int, float, float*)> Callback;
std::vector<std::unique_ptr<Callback>> m_functions;
// Add a callback to the object. Note the capture-less lambda.
template <typename H>
void add_callback_impl(H&& h) {
std::unique_ptr<Callback> callback (new Callback(std::forward<H>(h)));
add_object_callback(m_self, [](int a, float b, float* c, void* raw_ctx) {
return (*static_cast<Callback*>(raw_ctx))(a, b, c);
}, callback.get());
m_functions.push_back(std::move(callback));
}
public:
SomeObject() : m_self(create_object()) {}
~SomeObject() { free_object(m_self); }
// We create 4 public overloads to add_callback:
// This only accepts function objects having 2 arguments.
template <typename H>
auto add_callback(H&& h) -> decltype(h(1, 10.f), void()) {
using namespace std::placeholders;
add_callback_impl(std::bind(std::forward<H>(h), _1, _2));
}
// This only accepts function objects having 3 arguments.
template <typename H>
auto add_callback(H&& h) -> decltype(h(1, 1.0f, (float*)0), void()) {
add_callback_impl(std::forward<H>(h));
}
// This only accepts function pointers.
void add_callback(int(*h)(int, float)) const {
add_object_callback(m_self, [](int a, float b, float* c, void* d) {
return reinterpret_cast<int(*)(int, float)>(d)(a, b);
}, reinterpret_cast<void*>(h));
}
// This only accepts function pointers.
void add_callback(int(*h)(int, float, float*)) const {
add_object_callback(m_self, [](int a, float b, float* c, void* d) {
return reinterpret_cast<int(*)(int, float, float*)>(d)(a, b, c);
}, reinterpret_cast<void*>(h));
}
// Note that the last 2 overloads violates the C++ standard by assuming
// sizeof(void*) == sizeof(func pointer). This is valid in POSIX, though.
struct someobject* get_raw_object() const {
return m_self;
}
};
So the init() becomes:
void init(SomeObject& so) {
// A functor class
class test3 { ... };
so.add_callback(test1);
so.add_callback(test2);
// Some lambda context!
int j = 5;
so.add_callback(test3(j));
so.add_callback([j](int a, float b) -> int {
printf("test4 -- a: %d, b: %f", a, b);
return a*b*j;
});
so.add_callback([j](int a, float b, float *c) -> int {
printf("test5 -- a: %d, b: %f", a, b);
*c = a*b*j;
return a*b*j;
});
}
The full testing code (I'm not putting that to ideone here, because g++ 4.5 doesn't support implicitly converting a lambda to a function pointer, nor the range-based for.)
#include <vector>
#include <functional>
#include <cstdio>
#include <memory>
struct someobject;
struct someobject* create_object(void);
void free_object(struct someobject* obj);
void add_object_callback(struct someobject* obj,
int(*callback)(int, float, float*, void*),
void* context);
class SomeObject {
// The real object being wrapped.
struct someobject* m_self;
// The vector of callbacks which requires destruction. This vector is only a
// memory store, and serves no purpose otherwise.
typedef std::function<int(int, float, float*)> Callback;
std::vector<std::unique_ptr<Callback>> m_functions;
// Add a callback to the object. Note the capture-less lambda.
template <typename H>
void add_callback_impl(H&& h) {
std::unique_ptr<Callback> callback (new Callback(std::forward<H>(h)));
add_object_callback(m_self, [](int a, float b, float* c, void* raw_ctx) {
return (*static_cast<Callback*>(raw_ctx))(a, b, c);
}, callback.get());
m_functions.push_back(std::move(callback));
}
public:
SomeObject() : m_self(create_object()) {}
~SomeObject() { free_object(m_self); }
// We create 4 public overloads to add_callback:
// This only accepts function objects having 2 arguments.
template <typename H>
auto add_callback(H&& h) -> decltype(h(1, 10.f), void()) {
using namespace std::placeholders;
add_callback_impl(std::bind(std::forward<H>(h), _1, _2));
}
// This only accepts function objects having 3 arguments.
template <typename H>
auto add_callback(H&& h) -> decltype(h(1, 1.0f, (float*)0), void()) {
add_callback_impl(std::forward<H>(h));
}
// This only accepts function pointers.
void add_callback(int(*h)(int, float)) const {
add_object_callback(m_self, [](int a, float b, float* c, void* d) {
return reinterpret_cast<int(*)(int, float)>(d)(a, b);
}, reinterpret_cast<void*>(h));
}
// This only accepts function pointers.
void add_callback(int(*h)(int, float, float*)) const {
add_object_callback(m_self, [](int a, float b, float* c, void* d) {
return reinterpret_cast<int(*)(int, float, float*)>(d)(a, b, c);
}, reinterpret_cast<void*>(h));
}
// Note that the last 2 overloads violates the C++ standard by assuming
// sizeof(void*) == sizeof(func pointer). This is required in POSIX, though.
struct someobject* get_raw_object() const {
return m_self;
}
};
//------------------------------------------------------------------------------
int test1(int a, float b) {
printf("test1 -- a: %d, b: %f", a, b);
return a*b;
}
int test2(int a, float b, float *c) {
printf("test2 -- a: %d, b: %f", a, b);
*c = a*b;
return a*b;
}
void init(SomeObject& so) {
// A functor class
class test3
{
public:
test3(int j) : _j(j) {};
int operator () (int a, float b)
{
printf("test3 -- a: %d, b: %f", a, b);
return a*b*_j;
}
private:
int _j;
};
so.add_callback(test1);
so.add_callback(test2);
// Some lambda context!
int j = 5;
so.add_callback(test3(j));
so.add_callback([j](int a, float b) -> int {
printf("test4 -- a: %d, b: %f", a, b);
return a*b*j;
});
so.add_callback([j](int a, float b, float *c) -> int {
printf("test5 -- a: %d, b: %f", a, b);
*c = a*b*j;
return a*b*j;
});
}
//------------------------------------------------------------------------------
struct someobject {
std::vector<std::pair<int(*)(int,float,float*,void*),void*>> m_callbacks;
void call() const {
for (auto&& cb : m_callbacks) {
float d=0;
int r = cb.first(2, 3, &d, cb.second);
printf(" result: %d (%f)\n", r, d);
}
}
};
struct someobject* create_object(void) {
return new someobject;
}
void free_object(struct someobject* obj) {
delete obj;
}
void add_object_callback(struct someobject* obj,
int(*callback)(int, float, float*, void*),
void* context) {
obj->m_callbacks.emplace_back(callback, context);
}
//------------------------------------------------------------------------------
int main() {
SomeObject so;
init(so);
so.get_raw_object()->call();
}
Related
Suppose I am trying to use a function which accepts a binary function and calls it with some arguments:
typedef double (*BinaryFunction)(double a, double b);
typedef double (*UnaryFunction)(double a);
// Can't change this
double ExternalFunction(BinaryFunction binaryFunction)
{
return binaryFunction(1, 2);
}
Now suppose a user of my code is going to provide me with a unary function. My goal is to convert it into a binary function so that I can call ExternalFunction with it:
double MyFunction(UnaryFunction unaryFunction)
{
BinaryFunction binaryFunction = /* want a function (a, b) -> unaryFunction(a + b) */;
return ExternalFunction(binaryFunction);
}
How do I do this? Just to be clear, I understand that this would be easy if the unary function were known at compile time, but it's not - it will be an argument to my function. Thanks in advance.
Here's a summary of my attempts. I believe I understand why these don't work, but I'm providing them so you can see what I've been thinking so far.
I can't use a lambda, because I'd have to capture UnaryFunction, and capturing lambdas can't be converted to function pointers:
double MyFunction(UnaryFunction unaryFunction)
{
BinaryFunction binaryFunction = [unaryFunction](double a, double b){ return unaryFunction(a + b); };
return ExternalFunction(binaryFunction);
}
Use std::function ? Can't get that to work either:
void MyFunction(UnaryFunction unaryFunction)
{
std::function<double(double, double)> binaryFunctionTemp = [unaryFunction](double a, double b)
{
return unaryFunction(a + b);
};
BinaryFunction binaryFunction = binaryFunctionTemp.target<double(double, double)>();
ExternalFunction(binaryFunction);
}
What about a function object? Won't work because we'd need a pointer to a member function:
class BinaryFromUnary
{
public:
BinaryFromUnary(UnaryFunction unaryFunction) : unary_(unaryFunction) {};
double operator()(double a, double b)
{
return unary_(a + b);
}
private:
UnaryFunction unary_;
};
void MyFunction(UnaryFunction unaryFunction)
{
BinaryFromUnary functionObject(unaryFunction);
std::function<double(double, double)> binaryFunction = functionObject;
ExternalFunction(binaryFunction.target<double(double, double)>());
}
Even had a go with std::bind (and probably messed it up):
struct Converter {
Converter(UnaryFunction unary) : unary_(unary) {}
double binary(double a, double b) const { return unary_(a + b); }
UnaryFunction unary_;
};
void MyFunction(UnaryFunction unaryFunction)
{
Converter converter(unaryFunction);
std::function<double(double, double)> binaryFunction = std::bind( &Converter::binary, converter, _1, _2);
ExternalFunction(binaryFunction.target<double(double, double)>());
}
Tried a couple of other things along the same lines. Any ideas would be much appreciated.
Use an external variable to hold the unary function.
Include standard disclaimers about how inelegant and non-thread safe this is, etc. but at least this is a hack consistent with the stated requirements:
#include <iostream>
typedef double (*BinaryFunction)(double a, double b);
typedef double (*UnaryFunction)(double a);
// Can't change this
double ExternalFunction(BinaryFunction binaryFunction) {
return binaryFunction(1, 2);
}
namespace foo {
thread_local UnaryFunction unaryFunction;
}
double MyBinaryFunction(double a, double b) {
return foo::unaryFunction(a + b);
}
double MyUnaryFunction(double a) {
return 2 * a;
}
double MyFunction(UnaryFunction unaryFunction) {
foo::unaryFunction = unaryFunction;
BinaryFunction binaryFunction = MyBinaryFunction;
return ExternalFunction(binaryFunction);
}
int main() {
std::cout << MyFunction(MyUnaryFunction) << std::endl; // 6
return 0;
}
I don't know your exact use-case, but there's a chance this might help you:
typedef double (*BinaryFunction)(double a, double b);
typedef double (*UnaryFunction)(double a);
// Can't change this
double ExternalFunction(BinaryFunction binaryFunction)
{
return binaryFunction(1, 2);
}
// If you always know the unary function at compile time:
// Create a wrapper function with the BinaryFunction signature that takes
// a unary function as a NTTP:
template <UnaryFunction unaryFunction>
double wrapper(double a, double b)
{
return unaryFunction(a + b);
}
// Simply use this wrapper to implement MyFunction as follows:
template <UnaryFunction unaryFunction>
double MyFunction()
{
return ExternalFunction(wrapper<unaryFunction>);
}
// Using it:
double unary1(double x) { return x * 2; }
double unary2(double x) { return x * 3; }
int main()
{
std::cout << MyFunction<unary1>() << '\n';
std::cout << MyFunction<unary2>() << '\n';
}
Have a godbolt link to play around with it as well.
Unlike the other answer, this doesn't require a global, but this also only works if you always know your function at compile-time, which there's a good chance you don't, so sorry in advance. Hope it was still interesting.
How do I get the below code example, that compiles and works just fine to work inside a class?
Below code works just fine
#include <iostream>
using namespace std;
typedef int (*IntFunctionWithOneParameter) (int a);
int function(int a){ return a; }
int functionTimesTwo(int a){ return a*2; }
int functionDivideByTwo(int a){ return a/2; }
void main()
{
IntFunctionWithOneParameter functions[] =
{
function,
functionTimesTwo,
functionDivideByTwo
};
for(int i = 0; i < 3; ++i)
{
cout << functions[i](8) << endl;
}
}
So the above code works fine, but I want to move it inside a class in a separate file, similar to the below NON-WORKING IDEA, where I get "incomplete type is not allowed" error at "functions[] =";
class myClass {
private:
typedef int (*IntFunctionWithOneParameter) (int a);
int function(int a){ return a; }
int functionTimesTwo(int a){ return a*2; }
int functionDivideByTwo(int a){ return a/2; }
IntFunctionWithOneParameter functions[] =
{
function,
functionTimesTwo,
functionDivideByTwo
};
};
So my question is how can I get it to work inside my class, where it is the ONLY place the functions are needed, meaning I do need to access the functions in main() or other places!
EDIT
Here is why I need an "array of functions". To save time spent on "if's" or more exactly "switches" as I am making a software (vst) synthesizer, and the less time spent in the processing, the more notes (polyphonic) the user can play at any given time. And multiply the 44100 times per second the function is run, with 8 tone generators, which each can have up to 16 unison voices, so actually the function needed, may be called up to 5,644,800 times per second, per note played! The exact function needed inside this main loop is known BEFORE entering loop, and ONLY changes when the user adjust a knob, so I do want to avoid ifs and switches. Now had it only been one function that occasionally changes, i could just duplicate main loop with variations for each function possible, HOWEVER the main audio processing loop, has several areas, each with a variety of ever growing functions possible, each which ONLY changes when user changes various knobs. So although I could, I am not going to make 5 * 20 * 23 (and growing) different versions of a main loop, to avoid if's and switches.
There's a bunch of things wrong with the code that you posted:
No semicolon after class definition.
Class instead of class
No fixed size set for the functions member, which is not allowed. You need to explicitly set the size of the array.
Member function pointers are not the same as "regular" function pointers. Member function pointers have an implicit this as first argument, since they need an object to be invoked on. So myFunction is not of type myArrayOfFunctions. If you make myFunction and myFunction2 static, then they can be stored as regular function pointers. Is this an option?
The name myArrayOfFunctions is very confusing, since it's not an array at all.
All but the last of these will cause your code not to compile.
This example may be what you've needed.
Note: I've changed typedef statement to using and changed function's signatures to take in plain int for testing convinience sake.
class myClass {
public:
using myArrayOfFunctions = float(myClass::*)(int a, int b, float c);
float myFunction1 (int a, int b, float c)
{
return a * b * c;
}
float myFunction2 (int a, int b, float c)
{
return a + b + c;
}
myArrayOfFunctions functions[2];
myClass()
{
functions[0] = &myClass::myFunction1;
functions[1] = &myClass::myFunction2;
};
void Invoke()
{
(this->*functions[0])(1, 2, 3);
(this->*functions[1])(3, 2, 1);
}
};
int main()
{
myClass a;
a.Invoke();
(a.*(a.functions[0]))(4, 5, 6);
return 0;
}
As you see, I'm getting the pointer to the class function but to call it I need to call it with an actual object (this in invoke() function and a object in main()).
You can write this:
class myClass
{
public:
typedef float (*myArrayOfStaticFunctions) (int& a, int& b, float& c);
typedef float (myClass::*myArrayOfFunctions) (int& a, int& b, float& c);
static float myFunction1 (int& a, int& b, float& c){cout<<"myFunction1"<<endl; return 0;}
static float myFunction2 (int& a, int& b, float& c){ cout<<"myFunction2"<<endl; return 0;}
float myFunction3 (int& a, int& b, float& c){ cout<<"myFunction3"<<endl; return 0;}
float myFunction4 (int& a, int& b, float& c){ cout<<"myFunction4"<<endl; return 0;}
myArrayOfStaticFunctions StaticArrayfunctions[2];
myArrayOfFunctions Arrayfunctions[2];
myClass (){
StaticArrayfunctions [0] =myFunction1;
StaticArrayfunctions [1] =myFunction2;
Arrayfunctions [0] = &myClass::myFunction3;
Arrayfunctions [1] = &myClass::myFunction4;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
myClass m;
int a =0, b=0; float c;
m.StaticArrayfunctions[0] (a,b,c);
m.StaticArrayfunctions[1] (a,b,c);
myClass::myArrayOfFunctions func3 = m.Arrayfunctions[0];
myClass::myArrayOfFunctions func4 = m.Arrayfunctions[1];
(m.*func3)(a,b,c);
(m.*func4)(a,b,c);
return 0;
}
Suppose the "standard" C++ inheritance paradigm:
struct GeneralFunc
{
/*..members..*/
virtual double value(double a, double b) { return 0; }
};
struct Func_classA : GeneralFunc
{
/*..members..*/
double value(double a, double b) { return a * b; }
};
struct Func_classB : GeneralFunc
{
/*..members..*/
double value(double a, double b) { return a + b; }
};
void main(){
double a = 1.0, b = 1.0;
std::vector<GeneralFunc*> my_functions;
//fill my_functions from input
for (auto& f : my_functions)
{
double v = f->value(a, b);
}
}
I would like an implementation that is most efficient for the iteration, i.e. minimizes indirect references, maximizes inline optimizations, ect. To constrain the problem, I know beforehand each specific "type" I want to implement (I can define only the "func" types I require, without having to allow other possibilities).
several options appear available:
boost::polycollection
#include <boost/poly_collection/base_collection.hpp>
//...rest the same
boost::base_collection<GeneralFunc> my_functions
//...rest the same
std::variant
#include <variant>
//...rts
using funcs = std::variant<Func_classA, Func_classB /*..possibly more../*>
std::vector<funcs> my_functions
or CRTP (Curiously Recurring Template Pattern)
Let me know the correct nomenclature for this, but here I "upcast" the base class based on the "type" -- a kind of manual dispatch.
template<typename T>
struct GeneralFunc
{
/*..members..*/
int my_type;
double value(double a, double b) {
switch (my_type){
case TYPE_A:
return static_cast<Func_classA*>(this)->value(a,b);
/*..you get the idea..*/
I'm okay sacrificing marginal efficiency for ease of development, but is there a consensus on the "best practice" in this case?
EDITS* fixed some typos; my current development is "in-development" of CRTP the last option.
SOLUTION:
After testing, both boost::polycollection and std::variant are valid approaches. However, this turned out to be far most efficient (from memory, may be slightly off).
enum ftype { A = 0, B, C };
struct GeneralFunc
{
ftype my_type;
GeneralFunc(ftype t) : my_type(t) {}
inline double value(double a, double b) const; // delay definition until derived classes are defined
}
struct Func_classA : GeneralFunc
{
Func_classA() : GeneralFunc(ftype::A) {}
inline double value(double a, double b) const { return a * b; }
}
/* define B, C (& whatever) */
inline double GeneralFunc::value(double a, double b)
{
switch(my_type){
case (ftype::A):
return static_cast<Func_classA*>(this)->value(a,b);
/* same pattern for B, C, ect */
}
}
void main(){
std::vector<std::unique_ptr<GeneralFunc>> funcs;
funcs.push_back(std::make_unique<Func_classA>());
funcs.push_back(std::make_unique<Func_classB>());
funcs[0]->value(1.0,1.0); // calls Func_classA.value
funcs[1]->value(1.0,1.0); // calls Func_classB.value
}
I'd be tempted to just use std::function as the container, rather than re-writing it.
using GeneralFunc = std::function<double(double, double);
struct Func_classA
{
/*..members..*/
double value(double a, double b) { return a * b; }
/*explicit*/ operator GeneralFunc () const { return [this](double a, double b){ value(a, b) }; }
};
struct Func_classB
{
/*..members..*/
double value(double a, double b) { return a + b; }
/*explicit*/ operator GeneralFunc () const { return [this](double a, double b){ value(a, b) }; }
};
void main(){
double a = 1.0, b = 1.0;
std::vector<GeneralFunc> my_functions;
//fill my_functions from input
for (auto& f : my_functions)
{
double v = f(a, b);
}
}
I think there's an option you didn't include (which is the one I'd use for performance critical code), that is to create a tuple of function objects and "iterate" over such tuple. Unfortunately there is no nice API to iterate over a tuple, so one has to implement his own. See the snippet below
#include <tuple>
#include <functional>
template<int ... Id, typename Functions>
auto apply(std::integer_sequence<int, Id ...>, Functions& my_functions, double& v, double a, double b){
([](auto a, auto b){a=b;}(v, std::get<Id>(my_functions)( a, b )), ...);
}
int main(){
auto fA = [](double a, double b){return a*b;};
auto fB = [](double a, double b){return a+b;};
//create the tuple
auto my_functions=std::make_tuple(fA, fB);
double v=0;
double a = 1.;
double b = 1.;
//iterate over the tuple
apply(std::make_integer_sequence<int, 2>(), my_functions, v, a, b);
}
This way you create a type safe zero overhead abstraction, since the compiler knows everything about the types you use (you don't need any type erasure mechanism). Also there's no need of virtual functions (same as in CRTP), so the compiler will probably inline the function calls. The snippet above uses C++17 generic lambdas, could be also implemented in C++14 or C++11 compliant way, but it would be more verbose. I would prefer this over CRTP because to me it looks more readable: no static cast to the derived class, and no artificial hierarchy of inheritance.
EDIT: from your answer looks like you don't really need the CRTP here, what you write using the CRTP solution is equivalent to this
enum ftype { A = 0, B, C };
auto fA = [](double a, double b){return a*b;};
auto fB = [](double a, double b){return a+b;};
int main(){
std::vector<ftype> types(2);
types[0]=A;
types[1]=B;
auto value = [&types](double a, double b, ftype i){
switch(i){
case (ftype::A):
return fA(a,b);
break;
case (ftype::B):
return fB(a,b);
break;
}
};
double v=value(1., 1., A);
v=value(1., 1., B);
}
Might be a matter of taste, but I think the version above is more readable (you don't really need a common base class, or static cast to the derived class).
I want to have two version a function by hiding or not hiding several lines of code at the middle and also near the end of the function with template or other means. How to do that?
The functions are performance critical. They got run a gazillion of gazillion times.
You could implement the first one with a default parameter:
int A(int a, int b, int c, int d = 0)
{
// stuff
}
You can factor out the common part and use a function object to pass the differing part:
template<class Fun>
int AB(Fun&& fun){
int x = 0;
Outer Loop{
Inner Loop{
x = std::forward<Fun>(fun)();
}
}
return x;
}
int a, b, c, d;
// ... initialize
int result1 = AB([a,b,c]{
return a + b;
});
int result2 = AB([a,b,c,d]{
return a + b + c*d;
});
You could make this even more general by templating the return type.
There are no branches and an optimizing compiler will be able to expand the function call inline. The latter is the reason to prefer function objects to function pointers.
Honestly, I would suggest you to re-implement your code. But the one thing that is coming to my mind is using function as parameter, see below (you may also use lambda expression):
int fun1(int a, int b, int c, int d) {
return a + b;
}
int fun2(int a, int b, int c, int d) {
return a + b + c*d;
}
template <int (*FUN)(int, int, int, int)> int fun_main(int a, int b, int c, int d = 0) {
int x = 0;
Outer Loop{
Inner Loop{
x += FUN(a, b, c, d);
}
}
return x;
}
Then somwhere in your code:
fun_main<fun1>(1, 2, 3, 4);
I use Visual C++ 2012. I want to parameterize a template function with a pointer to another function. It all works nicely outside of a class:
int add(int a, int b) {
return a + b;
}
typedef int (*func)(int a, int b);
template<func F> int do_it(int a, int b) {
return F(a, b);
}
int foo(int a, int b) {
return do_it<add>(a, b);
}
Visual C++ 2012 compiles and optimizes that perfectly.
Now, I put it inside a class and adjust it for pointer-to-member:
struct S {
int add(int a, int b) {
return a + b;
}
typedef int (S::*func)(int a, int b);
template<func F> int do_it(int a, int b) {
return F(a, b); // <-- here be error!
}
int foo(int a, int b) {
return do_it<&S::add>(a, b);
}
};
S s;
int bar(int a, int b) {
return s.foo(a, b);
}
But that gives me a compiler-error:
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.51106.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
x2.cpp
x2.cpp(7) : error C2064: term does not evaluate to a function taking 2 arguments
x2.cpp(10) : see reference to function template instantiation 'int S::do_it<int S::add(int,int)>(int,int)' being compiled
Any idea why and how to fix it?
The syntax for pointers to members still apply. You should write :
typedef int (S::*func)(int a, int b);
template<func F> int do_it(int a, int b) {
return (this->*F)(a, b);
}
Using a pointer to member always require associating an object with it - here, it makes sense you would call your pointer on this.
Member-function pointers must be called on an object; unlike a normal member function call, this must be specified explicitly:
(this->*F)(a,b);
Although, if the function doesn't need to access this, perhaps it should be static (or a non-member), specified by a normal function pointer.
It looks like you just need to call the member function pointer on an object:
template<func F> int do_it(int a, int b) {
return (this->*F)(a, b);
}
Pointers to members always need to be called on an object, unlike free and static member functions, which is why you need the above syntax.
You need to provide the object when calling a pointer-to-meber function:
(this->*F)(a, b);
One point further: write your "do_it" more generally as
template<class F> int do_it(F f, int a, int b) {
return f(a, b); }
Now, you can pass it any function of two integers:
int mul(int a, int b) { return a*b ; }
S s;
s.do_it(mul,2,3);
When you intend to use it inside your class with a member function, just bind it with this. Using boost::bind,
int foo(int a, int b) {
return do_it( boost::bind(&S::add,this,_1,_2),a,b) ;