Function overloading inside overloaded function - c++

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).

Related

Sleek way to use template function as function argument?

What I really want to do is to compare the performance of different algorithms which solve the same task in different ways. Such algorithms, in my example called apply_10_times have sub algorithms, which shall be switchable, and also receive template arguments. They are called apply_x and apply_y in my example and get int SOMETHING as template argument.
I think the solution would be to specify a template function as template parameter to another template function. Something like this, where template_function is of course pseudo-code:
template<int SOMETHING>
inline void apply_x(int &a, int &b) {
// ...
}
template<int SOMETHING>
inline void apply_y(int &a, int &b) {
// ...
}
template<template_function APPLY_FUNCTION, int SOMETHING>
void apply_10_times(int &a, int &b) {
for (int i = 0; i < 10; i++) {
cout << SOMETHING; // SOMETHING gets used here directly as well
APPLY_FUNCTION<SOMETHING>(a, b);
}
}
int main() {
int a = 4;
int b = 7;
apply_10_times<apply_x, 17>(a, b);
apply_10_times<apply_y, 19>(a, b);
apply_10_times<apply_x, 3>(a, b);
apply_10_times<apply_y, 2>(a, b);
return 0;
}
I've read that it's not possible to pass a template function as a template parameter, so I can't pass APPLY_FUNCTION this way. The solution, afaik, is to use a wrapping struct, which is then called a functor, and pass the functor as a template argument. Here is what I got with this approach:
template<int SOMETHING>
struct apply_x_functor {
static inline void apply(int &a, int &b) {
// ...
}
};
template<int SOMETHING>
struct apply_y_functor {
static inline void apply(int &a, int &b) {
// ...
}
};
template<typename APPLY_FUNCTOR, int SOMETHING>
void apply_10_times(int &a, int &b) {
for (int i = 0; i < 10; i++) {
cout << SOMETHING; // SOMETHING gets used here directly as well
APPLY_FUNCTOR:: template apply<SOMETHING>(a, b);
}
}
This approach apparently works. However, the line APPLY_FUNCTOR:: template apply<SOMETHING>(a, b); looks rather ugly to me. I'd prefer to use something like APPLY_FUNCTOR<SOMETHING>(a, b); and in fact this seems possible by overloading the operator(), but I couldn't get this to work. Is it possible and if so, how?
As it is not clear why you need APPLY_FUNCTION and SOMETHING as separate template arguments, or why you need them as template arguments at all, I'll state the obvious solution, which maybe isn't applicable to your real case, but to the code in the question it is.
#include <iostream>
template<int SOMETHING>
inline void apply_x(int a, int b) {
std::cout << a << " " << b;
}
template<int SOMETHING>
inline void apply_y(int a, int b) {
std::cout << a << " " << b;
}
template<typename F>
void apply_10_times(int a, int b,F f) {
for (int i = 0; i < 10; i++) {
f(a, b);
}
}
int main() {
int a = 4;
int b = 7;
apply_10_times(a, b,apply_x<17>);
apply_10_times(a, b,apply_y<24>);
}
If you want to keep the function to be called as template argument you can use a function pointer as non-type template argument:
template<void(*F)(int,int)>
void apply_10_times(int a, int b) {
for (int i = 0; i < 10; i++) {
F(a, b);
}
}
int main() {
int a = 4;
int b = 7;
apply_10_times<apply_x<17>>(a, b);
apply_10_times<apply_y<24>>(a, b);
}
In any case I see no reason to have APPLY_FUNCTION and SOMETHING as separate template arguments. The only gain is more complex syntax which is exactly what you want to avoid. If you do need to infer SOMETHING from an instantiation of either apply_x or apply_y, this is also doable without passing the template and its argument separately, though again you'd need to use class templates rather than function templates.
PS:
Ah, now I understand what you mean. Yes, apply_10_times() also uses SOMETHING directly. Sorry, I simplified the code in the question too much.
As mentioned above. This does still not imply that you need to pass them separately. You can deduce SOMETHING from a apply_x<SOMETHING> via partial template specialization. This however requires to use class templates not function templates:
#include <iostream>
template <int SOMETHING>
struct foo {};
template <int X>
struct bar {};
template <typename T>
struct SOMETHING;
template <template <int> class T,int V>
struct SOMETHING<T<V>> { static constexpr int value = V; };
int main() {
std::cout << SOMETHING< foo<42>>::value;
std::cout << SOMETHING< bar<42>>::value;
}
What I really want to do is to compare the performance of different
algorithms which solve the same task in different ways.
You should provide more details about that.
Your first step should be get familiar with Google Benchmark. There is as site which provides it online. This tool give proper patterns for your scenario.
In next step you must be aware that in C and C++ there is "as if rule" which allows optimizer do do wonderful things, but makes creation of good performance test extremely difficult. It is easy write test which doesn't measure actual production code.
Here is cppcon talk showing how many traps are hidden when doing a good performance test fro C++ code. So be very very careful.

How to avoid code duplication with function overloading

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).

What is the difference between different ways of passing a function as an argument to another function?

I have the situation where one function calls one of several possible functions. This seems like a good place to pass a function as a parameter. In this Quoara answer by Zubkov there are three ways to do this.
int g(int x(int)) { return x(1); }
int g(int (*x)(int)) { return x(1); }
int g(int (&x)(int)) { return x(1); }
...
int f(int n) { return n*2; }
g(f); // all three g's above work the same
When should which method be used? What are there differences? I prefer the simplest approach so why shouldn't the first way always be used?
For my situation, the function is only called once and I'd like to keep it simple. I have it working with pass by pointer and I just call it with g(myFunc) where myFunc is the function that gets called last.
Expanding on L.F.'s comment, it's often better to eschew function pointers entirely, and work in terms of invocable objects (things which define operator()). All of the following allow you to do that:
#include <type_traits>
// (1) unrestricted template parameter, like <algorithm> uses
template<typename Func>
int g(Func x) { return x(1); }
// (2) restricted template parameter to produce possibly better errors
template<
typename Func,
typename=std::enable_if_t<std::is_invocable_r_v<int, Func, int>>
>
int g(Func x) { return std::invoke(x, 1); }
// (3) template-less, trading a reduction in code size for runtime overhead and heap use
int g(std::function<int(int)> x) { return x(1); }
Importantly, all of these can be used on lambda functions with captures, unlike any of your options:
int y = 2;
int ret = g([y](int v) {
return y + v;
});

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

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