How to avoid code duplication with function overloading - c++

I have a pair of overloaded functions:
void func(const std::string& str, int a, char ch, double d) {
// piece of code A
sendMsg(str, a, ch, d);
// piece of code B
}
void func(int a, char ch, double d) {
// piece of code A
sendMsg(a, ch, d);
// piece of code B
}
piece of code A and piece of code B are exactly the same, the only difference is the parameter of sendMsg.
Is there some way to avoid the code duplication?

template may be a possibility:
template <typename ... Ts>
auto func(const Ts&... args)
-> decltype(sendMsg(args...), void()) // SFINAE to only allow correct arguments
{
// piece of code A
sendMsg(args...);
// piece of code B
}
but moving // piece of code A in its own function would probably be my choice.

You would have to do something like
void codeA() {
// ...
}
void codeB() {
// ...
}
void func(const std::string& str, int a, char ch, double d) {
codeA();
sendMsg(str, a, ch, d);
codeB();
}
void func(int a, char ch, double d) {
codeA();
sendMsg(a, ch, d);
codeB();
}

Another idea would be to give a default value to str:
void func(int a, char ch, double d, const std::string& str = "")
{
// piece of code A
if (str.empty()) sendMsg(a, ch, d);
else sendMsg(str, a, ch, d);
// piece of code B
}

Of course, use a functor:
template <typename F> void func2(F&& f) {
// piece of code A
f();
// piece of code B
}
Usage:
void func(int a, char ch, double d) {
func2([&](){ sendMsg(a, ch, d); });
}
A bit of explanation: Currently accepted answer is totally fine when you need to call the exactly same code with different parameters. But when you need to "inject" an arbitrary code (possibly a multiple pieces of arbitrary code) into another function, passing a temporary lambda is your best bet. Conceptually, what receiving function is seeing/getting is some abstract "callable" object (in fact, it can be anything with operator (), not just lambda) which it calls at due time. And since its a templated function, it will be compiled into zero-overhead code "as if" actual code was copy-pasted in there. The usage part is simply shows a c++ syntax to create a callable with arbitrary code in-place (I advise to read language references/tutorials on lambdas to understand the internals better).

Related

Best way to make both a compile-time and runtime version of a function

I have a function that will be called by both compile-time and runtime functions (gtest and python ctypes). I need a templated version and one with the templated variables as function parameters. For example
template<int A, int B, int C>
void function_compiletime(int a, int b, int c) {
// code section 1
}
void function_runtime(int a, int b, int c, int A, int B, int C) {
// code section 2
}
Where // code section 1 is identical to // code section 2. I am cautious that I might accidentally alter something in // code section 1 and not in // code section 2. How can enforce that the body of the functions should be identical?
Best way to make both a compile-time and runtime version of a function
How can enforce that the body of the functions should be identical?
By defining a single constexpr function:
constexpr void
function_runtime(int a, int b, int c, int A, int B, int C)
{
// code section
}

Function overloading inside overloaded function

I am using C++14 (and pretty new to it). I have 3 overloaded functions func within which another overloaded function do_something gets called depending on its parent function (func).
int func(int a) {
bar(a);
foo();
}
int func(int a, float b) {
bar(a);
do_something(b);
foo();
}
int func(int a, float b, char c) {
bar(a);
do_something(b, c);
foo();
}
I see that the functionality within func is almost the same except which version of do_something gets called. Is there any way I can make this generic and combine all func's together?
To begin with, make func a template that accepts a parameter pack. The int a argument, the call to bar and the call to foo are always there, so that's simple. Let's add a placeholder for do_something for now.
template <class ...Args>
int func(int a, Args&&... other)
{
bar(a);
// somehow call do_something and do the right thing
foo();
return 0;
}
You want to instantiate and invoke the above template as before:
func(42);
func(42, 1.f);
func(42, 1.f, 'A');
Now let's tackle the call to do_something. If you simply add it in the middle of the new func template;
do_something(std::forward<Args>(other)...);
this refuses to compile for func(42), i.e., the case with only one argument. Hence, we need a special case for this. One way to achieve this in another level of indirection for do_something:
// No additional argument case, does nothing:
void do_something_wrapper() {}
// The other two cases
template <class ...Args>
void do_something_wrapper(Args&&... args)
{
do_something(std::forward<Args>(args)...);
}
And now, the placeholder from the func function template should be:
do_something_wrapper(std::forward<Args>(other)...);
The first step would be to use variadic-templates to take the part you want to forward to do_something:
template<class ... Args>
int func(int a, Args... args)
{
bar(a);
do_something(std::forward<Args>(args)...)
foo();
}
But now you have lost the argument types of func. So if this is a problem you will have to find a way to test them again.
Although I would probably go with the answer from generic_opto_guy myself, he's right to point out that you would lose the types in your interface. Depending on your situation, you might want to preserve this.
In that case, you can easily rework it to something akin to the following:
namespace details {
template<class ... Args>
int func_impl(int a, Args &&... args)
{
bar(a);
do_something(std::forward<Args>(args)...)
foo();
}
}
int func(int a) { return details::func_impl(a); }
int func(int a, float b) { return details::func_impl(a, b); }
int func(int a, float b, char c) { return details::func_impl(a, b, c); }
Note that the implementation has been adjusted to use perfect forwarding. While not required in this particular case, it is often useful in forwarding situations you might encounter in the future.
Again, unless you absolutely need to present a clear interface to client code, I would just go with the first implementation.
The other answers are good general purpose answers to the question as asked, but if you have a few "spare" values of c available, you could write something like:
int func(int a) {
func(a, 0.0,'\001');
}
int func(int a, float b, char c = '\002') {
bar(a);
switch (c) {
case '\001':
break; // func(a)
case '\002':
do_something(b): // func(a, b)
break;
default:
do_something(b, c); // func(a, b, c)
break;
}
foo();
}
Depending on the application, this may be simpler.
Is too late to play?
You tagged C++14 but, if you could use C++17, you could use if constexpr so
template <typename ... Ts>
int func (int a, Ts && ... ts)
{
bar(a);
if constexpr ( sizeof...(Ts) > 0u )
do_something(std::forward<Ts>(ts)...);
foo();
// and remember to return something of int
}
In C++14 you have to duplicate something somewhere.
Or you write two func()s
int func (int a)
{
bar(a);
foo();
// and remember to return something of int
}
template <typename ... Ts>
int func (int a, Ts && ... ts)
{
bar(a);
do_something(std::forward<Ts>(ts)...);
foo();
// and remember to return something of int
}
or you add a no-argument do_something()
void do_something ()
{ }
or, as suggested by lubgr, you call do_something() through a wrapper (creating a special no-argument wrapper).

Ignore reference function argument

I have function with this signature (I can not edit it):
void foo(int a,int b, int& c);
I want to call it but I do not care about the getting c. Currently I do this:
int temp;
foo(5,4,temp);
//temp never used again
My solution seems dumb. What is the standard way to ignore this argument.
There is none.
If your main concern is about polluting the current stack with a temp variable, a wrapper function like this should suffice:
void foo_wrapper(int a, int b)
{
int temp; foo(a, b, temp);
}
I would write an overload that turns the output argument into a normal return value. I really don't like output arguments and think that they should be avoided.
int foo(int a, int b) {
int tmp = 0;
foo(a,b, tmp);
return tmp;
}
In your program, you just this overload and either ignore the return value or use it.
This is an over engineered solution, so I don't actually recommend it as the first option in production code.
You can create a class to help you easily ignore these kinds of arguments:
template <class T>
struct RefIgnore
{
static inline T ignored_{};
constexpr operator T&() const
{
return ignored_;
}
};
template <class T>
constexpr RefIgnore<T> ref_ignore{};
void foo(int a,int b, int& c);
auto test()
{
foo(2, 3, ref_ignore<int>);
}
Instead of reference you can pass it as a pointer
void foo(int a,int b, int *c = NULL);
in calling place you can either have it as
foo(5, 6);
or if you want to pass the 3rd argument then you can have it as
int n = 3;
foo (1, 2, &n);

How to uniformly implement two-way conversion without code repetition?

I have two big C structures used in a C legacy code, and I need to convert from one to another, and the other way around. Something like this :
#include <iostream>
struct A {
int a;
float b;
};
struct B {
char a;
int b;
};
struct C {
A a;
B b;
};
struct D {
int a;
char b;
float c;
};
void CtoD( const C& c, D &d ) {
d.a = c.a.a;
d.b = c.b.a;
d.c = c.a.b;
}
void DtoC( const D &d, C& c ) {
c.a.a = d.a;
c.b.a = d.b;
c.a.b = d.c;
}
int main()
{
C c = { { 1, 3.3f }, { 'a', 4 } };
D d = { 1, 'b', 5.5f };
#if 0
CtoD( c, d );
#else
DtoC( d, c );
#endif
std::cout<<"C="<<c.a.a<<" "<<c.a.b<<" "<<c.b.a<<" "<<c.b.b<<std::endl;
std::cout<<"D="<<d.a<<" "<<d.b<<" "<<d.c<<std::endl;
}
Functions CtoD and DtoC are doing the same thing, but in opposite direction. Changing one structure requires changing both of them.
To minimize possibility of an error, and to avoid repetition, I would like to implement some kind of mapping, where I define the connections only once, and then I copy one value to another. This way, only one change is needed if a structure changes.
So, the question is : how to do it? Is there perhaps a design pattern I could use?
My real structures have hundreds of fields. The above is just simplified example.
In your literal example, I don't think it's worth the hassle. Just write tests so that you ensure your conversions work well.
In your real code, if your structs have "hundreds of fields", your structs may be badly designed. Maybe they should be composed of smaller objects. I've never designed anything which required hunderds of fields in exactly the same struct object - instead, these fields allowed some kind of classification so that they could be treated in smaller bunches.
Since your code is legacy and you don't want to rewrite it, just write tests for your conversions functions, as I said above for the example.
Well tested code is no longer legacy code. Legacy code is basically code for which you don't have automated tests.
If rewriting it is not an option, testing it is a must.
About the cost of testing it "both ways", Idan Arye's comment below says everything:
Since the conversion is symmetric, testing it both ways is not that
much more work than testing it one way. All you need to do is init two
structs - C c and D d - and set them to be the converted versions of
each other. Then you just have to check that CtoD(c)==d and
DtoC(d)==c (or use comparison functions if you happen to have them
defined). The big work here is initializing c and d - but you would
have to do that anyways if you wanted to test one way conversion, so
adding the test for the other way is very cheap.
Let's get naughty...
struct rightwards_t {} rightwards;
struct leftwards_t {} leftwards;
template<typename Left, typename Right>
inline void map_field(Left& left, const Right& right, leftwards_t) {
left = right;
}
template<typename Left, typename Right>
inline void map_field(const Left& left, Right& right, rightwards_t) {
right = left;
}
template<typename Direction>
void convert(C& c, D& d, Direction direction) {
map_field(c.a.a, d.a, direction);
map_field(c.b.a, d.b, direction);
map_field(c.a.b, d.c, direction);
}
// Usage
C c;
D d;
convert(c, d, leftwards); // Converts d into c
convert(c, d, rightwards); // Converts c into d
Really don't know if it works (no compiler at hand), but I wanted to write it. If anyone can help me make it correct, please do.
You could do it with a container of hundreds of std::pairs of references to the sub-objects involved. With a references, you can both read and write, so reading from the left-object and writing to the right-object converts one-way. The opposite convert the other way.
Pick your favorite scripting language(if you don't have one yet I recommend Ruby) and write a small script that generate the conversion functions for you(both source and header files).
Unless you pick a lame scripting language, you can even represent the connections directly in the language, when calling the functions that generate the converters. For example, in Ruby after defining generate_converters you could write:
generate_converters :C,:D do
convert 'a.a','a'
convert 'b.a','b'
convert 'a.b','c'
end
I agree with Daniel, not worth the hassle, but you could write a little app that generates the code for you. You feed the app with description of two structs, and bindings between struct members, and the app generates the C code that is then compiled as usual.
Another alternative is to fiddle with pointers to members, but that could consume even more developer's time, so is even less worth the hassle than the first option.
It took me a while to figure out how to do this. And I came out with next solution :
#include <iostream>
#include <algorithm>
#include <cstring>
struct A {
int a;
float b;
};
struct B {
char a;
int b;
};
struct C {
A a;
B b;
};
struct D {
int a;
char b;
float c;
};
template< typename T1, typename T2 >
struct DataField
{
static inline void Update( const T1 & src, T2 & dst ) { dst = src; }
static inline void Update( T1 & dst, const T2 & src ) { dst = src; }
};
template<>
struct DataField< const char*, char* >
{
static inline void Update( const char* src, char* dst ) { strcpy( dst, src ); }
};
template<>
struct DataField< char*, const char* >
{
static inline void Update( char* dst, const char* src ) { strcpy( dst, src ); }
};
template< typename T1, typename T2, int N >
struct DataField< T1[N], T2[N] >
{
static inline void Update( const T1 (&src)[N], T2 (&dst)[N] ) { std::copy_n( src, N, dst ); }
static inline void Update( T1 (&dst)[N], const T1 (&src)[N] ) { std::copy_n( src, N, dst ); }
};
template< typename T1, typename T2 >
void UpdateDataField( T1 & src, T2 & dst )
{
DataField< T1, T2 >::Update( src, dst );
}
template< typename T1, typename T2 >
void UpdateMappedDataFields( T1 & src, T2 & dst )
{
UpdateDataField( src.a.a, dst.a );
UpdateDataField( src.a.b, dst.c );
UpdateDataField( src.b.a, dst.b );
}
void CtoD( const C& c, D &d ) {
UpdateMappedDataFields( c, d );
}
void DtoC( const D &d, C& c ) {
UpdateMappedDataFields( c, d );
}
int main()
{
C c = { { 1, 3.3f }, { 'a', 4 } };
D d = { 1, 'b', 5.5f };
#if 0
CtoD( c, d );
#else
DtoC( d, c );
#endif
std::cout<<"C="<<c.a.a<<" "<<c.a.b<<" "<<c.b.a<<" "<<c.b.b<<std::endl;
std::cout<<"D="<<d.a<<" "<<d.b<<" "<<d.c<<std::endl;
}
All data fields mapping is done in the UpdateMappedDataFields function, and only there.
What I don't like is that the function UpdateMappedDataFields is a template, and the way it is implemented, it prevents autocomplete when using IDEs, since the types are not known.
However, I would still like to hear if there is a better way.
Similar to what Idan and Dialecticus proposed, you can also just use your editor's search and replace function:
E.g. write CtoD manually, copy the body to DtoC and - in eclipse - use
Find: ^(.*)=(.*);
Replace: $2=$1;
in order to automatically swap the left and right side of each assignment in the body of DtoC.
Whether or not this is preferable to the usage of more or less complex c++ constructs depends on your specific code and requirements. In my opinion, the code is easier to read and maintain this way, but of course nothing enforces coherency between CtoD and DtoC after future changes (I'd mention the procedure in a code comment).

wrapping C callbacks with C++ lambdas, possible to use template polymorphism?

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();
}