Is there a standalone implementation of std::function? - c++

I'm working on an embedded system, so code size is an issue. Using the standard library ups my binary size by about 60k, from 40k to 100k. I'd like to use std::function, but I can't justify it for 60k. Is there a standalone implementation that I can use, or something similar? I'm using it to implicitly cast lambdas in member functions with bound variables in c++ 11.

Here is simple implementation of std::function-like class template without inclusion of any headers. You can customize the behavior as you wish(like move/forward, empty call response, etc):
live_demo
// Scroll down for example of usage
namespace bicycle
{
template<typename Result,typename ...Args>
struct abstract_function
{
virtual Result operator()(Args... args)=0;
virtual abstract_function *clone() const =0;
virtual ~abstract_function() = default;
};
template<typename Func,typename Result,typename ...Args>
class concrete_function: public abstract_function<Result,Args...>
{
Func f;
public:
concrete_function(const Func &x)
: f(x)
{}
Result operator()(Args... args) override
{
return f(args...);
}
concrete_function *clone() const override
{
return new concrete_function{f};
}
};
template<typename Func>
struct func_filter
{
typedef Func type;
};
template<typename Result,typename ...Args>
struct func_filter<Result(Args...)>
{
typedef Result (*type)(Args...);
};
template<typename signature>
class function;
template<typename Result,typename ...Args>
class function<Result(Args...)>
{
abstract_function<Result,Args...> *f;
public:
function()
: f(nullptr)
{}
template<typename Func> function(const Func &x)
: f(new concrete_function<typename func_filter<Func>::type,Result,Args...>(x))
{}
function(const function &rhs)
: f(rhs.f ? rhs.f->clone() : nullptr)
{}
function &operator=(const function &rhs)
{
if( (&rhs != this ) && (rhs.f) )
{
auto *temp = rhs.f->clone();
delete f;
f = temp;
}
return *this;
}
template<typename Func> function &operator=(const Func &x)
{
auto *temp = new concrete_function<typename func_filter<Func>::type,Result,Args...>(x);
delete f;
f = temp;
return *this;
}
Result operator()(Args... args)
{
if(f)
return (*f)(args...);
else
return Result{};
}
~function()
{
delete f;
}
};
}
// ___________________[ Example of usage ]___________________ //
int func1(double)
{
return 1;
}
struct Functor2
{
int operator()(double)
{
return 2;
}
};
double func3(bool,int)
{
return 3.0;
}
struct Functor4
{
double operator()(bool,int)
{
return 4.0;
}
};
int main()
{
int res = 10;
{
bicycle::function<int(double)> f{func1};
res -= f(1.0);
f = Functor2{};
res -= f(2.0);
}
{
bicycle::function<double(bool,int)> f1;
f1 = func3;
bicycle::function<double(bool,int)> f2{f1};
res -= f2(true,1);
f1 = Functor4{};
f2 = f1;
res -= f2(false,2);
}
return res;
}

The 60k came from exception handling being added by the compiler, because exceptions were required for std::function. std::function only throws one exception, "bad_function_call". So I removed the code that threw the exception, now it seg faults if an empty function is called, and I saved myself 60k.

Related

dynamically call same named function with different return type

I have a situation here...
I want to design a Factory where I can call a function with same name and no parameters but return different data Types. Based on the SubClassName I need to instantiate the Object.
Need help or lead on any design pattern to follow?
EDIT:
An abstract pseudo code...
class parent{
public:
virtual string getName() = 0;
//some virtual function.. not sure how to design. As the return type is dynamic.
*** getValue(){}
};
class A : public parent{
int x;
public:
virtual string getName(){ return "A";}
virtual int getValue(){retun x;}
};
class B : public parent{
string s;
public:
virtual string getName(){ return "B";}
virtual string getValue(){ return s;}
};
void main(){
string callingClass = "B";
parent * arrayPtrs[2];
arrayPtrs[0] = new A;
arrayPtrs[1] = new B;
for (loop through array, through iterator i){
if(arrayPtrs[i]->getName == callingClass ){
cout<<arrayPtrs[i]->getValue;
}
}
}
In C++ a function can only have one return type at a time, and you cannot change that dynamically.
However - as suggested by #mch - you can use template specializations. Keep in mind though, that this method is not dynamic. Your functions will be generated at compile time.
If I understood your question correctly, maybe this can be of help.
class MyObject1
{
//...
};
class MyObject2
{
//...
};
template<typename T>
struct Factory
{
constexpr static T gen();
};
template<>
struct Factory<MyObject1>
{
constexpr static MyObject1 gen()
{
return MyObject1(/*... whatever parameters you see fit ...*/);
}
};
template<>
struct Factory<MyObject2>
{
constexpr static MyObject2 gen()
{
return MyObject2(/*... whatever parameters you see fit ...*/);
}
};
int main()
{
auto myObj = Factory<MyObject1>::gen();
return 0;
}
Although this method seems fairly useless to me. You could simply call the desired constructor instead of this.
But then again, I'm not sure if this is what you thought of. If I made any mistakes please feel free, to correct me. I'll try to edit my answer best as I can.
EDIT:
To keep the virtual functionality too, the only way I can think of is type erasure: see https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure
The closest I could get to what you've asked for is this:
#include <iostream>
#include <string>
#include <any>
class parent {
public:
// you can use this too but I think type checking is more handy
// see in main function
/* virtual std::string getName() const = 0; */
virtual std::any getValue() const = 0;
};
class A : public parent {
public:
typedef int value_type;
private:
value_type x;
public:
A(value_type x) :
x(x)
{}
/* virtual std::string getName() const override { return "A"; } */
virtual std::any getValue() const override
{ return this->x; }
};
class B : public parent {
public:
typedef std::string value_type;
private:
value_type s;
public:
B(const value_type& s) :
s(s)
{}
/* virtual std::string getName() const override { return "B"; } */
virtual std::any getValue() const override
{ return this->s; }
};
int main(){
using callingClass = A;
parent* arrayPtrs[2];
arrayPtrs[0] = new A(42);
arrayPtrs[1] = new B("my string");
for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
{
// Note:
// dynamic cast will return nullptr if $callingClass
// is not a derived class
if (dynamic_cast<callingClass*>(arrayPtrs[i]))
std::cout << std::any_cast<callingClass::value_type>(arrayPtrs[i]->getValue()) << std::endl;
}
return 0;
}
I hope this one helps.
Note, that I used dynamic_cast to check the correct type. If you know a better solution, you can use that, too. But under these circumstances I couldn't think of any better.
EDIT2:
#include <iostream>
#include <string>
#include <tuple>
class some
{
using id = size_t;
template<typename T>
struct type { static void id() { } };
template<typename T>
static id type_id() { return reinterpret_cast<id>(&type<T>::id); }
template<typename T>
using decay = typename std::decay<T>::type;
template<typename T>
using none = typename std::enable_if<!std::is_same<some, T>::value>::type;
struct base
{
virtual ~base() { }
virtual bool is(id) const = 0;
virtual base *copy() const = 0;
} *p = nullptr;
template<typename T>
struct data : base, std::tuple<T>
{
using std::tuple<T>::tuple;
T &get() & { return std::get<0>(*this); }
T const &get() const& { return std::get<0>(*this); }
bool is(id i) const override { return i == type_id<T>(); }
base *copy() const override { return new data{get()}; }
};
template<typename T>
T &stat() { return static_cast<data<T>&>(*p).get(); }
template<typename T>
T const &stat() const { return static_cast<data<T> const&>(*p).get(); }
template<typename T>
T &dyn() { return dynamic_cast<data<T>&>(*p).get(); }
template<typename T>
T const &dyn() const { return dynamic_cast<data<T> const&>(*p).get(); }
public:
some() { }
~some() { delete p; }
some(some &&s) : p{s.p} { s.p = nullptr; }
some(some const &s) : p{s.p->copy()} { }
template<typename T, typename U = decay<T>, typename = none<U>>
some(T &&x) : p{new data<U>{std::forward<T>(x)}} { }
some &operator=(some s) { swap(*this, s); return *this; }
friend void swap(some &s, some &r) { std::swap(s.p, r.p); }
void clear() { delete p; p = nullptr; }
bool empty() const { return p; }
template<typename T>
bool is() const { return p ? p->is(type_id<T>()) : false; }
template<typename T> T &&_() && { return std::move(stat<T>()); }
template<typename T> T &_() & { return stat<T>(); }
template<typename T> T const &_() const& { return stat<T>(); }
template<typename T> T &&cast() && { return std::move(dyn<T>()); }
template<typename T> T &cast() & { return dyn<T>(); }
template<typename T> T const &cast() const& { return dyn<T>(); }
template<typename T> operator T &&() && { return std::move(_<T>()); }
template<typename T> operator T &() & { return _<T>(); }
template<typename T> operator T const&() const& { return _<T>(); }
};
using any = some;
class parent {
public:
// you can use this too but I think type checking is more handy
/* virtual std::string getName() const = 0; */
virtual any getValue() const = 0;
};
class A : public parent {
public:
typedef int value_type;
private:
value_type x;
public:
A(value_type x) :
x(x)
{}
/* virtual std::string getName() const override { return "A"; } */
virtual any getValue() const override
{ return this->x; }
};
class B : public parent {
public:
typedef std::string value_type;
private:
value_type s;
public:
B(const value_type& s) :
s(s)
{}
/* virtual std::string getName() const override { return "B"; } */
virtual any getValue() const override
{ return this->s; }
};
int main(){
using callingClass = A;
parent* arrayPtrs[2];
arrayPtrs[0] = new A(42);
arrayPtrs[1] = new B("my string");
for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
{
// Note:
// dynamic cast will return nullptr if $callingClass
// is not a derived class
if (dynamic_cast<callingClass*>(arrayPtrs[i]))
std::cout << arrayPtrs[i]->getValue()._<callingClass::value_type>() << std::endl;
}
return 0;
}
This snipped is in case you cannot use C++17 features, and is based on:
any class

Why can't I have std::optional<T> where T is abstract?

This does not work:
struct Type {
virtual bool func(const std::string& val) const noexcept = 0;
}
// in main
optional<Type> = some_function_returning_optional_type();
and fails with a error message:
error: cannot declare field 'std::experimental::fundamentals_v1::_Optional_base<Type, false>::<anonymous union>::_M_payload' to be of abstract type 'Type'
Changing the Type to have a non-pure function works, but is not appropriate in this case, because there cannot be an instance of Type in my code, only classes which inherit from it should be able to exist.
std::optional<T> stores its value in-place - it therefore needs to know the size of T to work correctly, and T must be a concrete type that can be instantiated. You can think of std::optional<T> as:
template <typename T>
struct optional
{
std::aligned_storage_t<sizeof(T), alignof(T)> _data;
bool _set;
};
An abstract type represents an interface - polymorphism and some sort of indirection are required to work with abstract types. std::optional doesn't have any indirection by design.
Your proposal of optional will of course work but it would offend me to have to write
x.value()->do_something();
and I'd be concerned that users might do something daft:
x.value().reset(); // now what?
We can achieve polymorphism with a non-polymorphic interface by using a wrapper.
Here's one way:
#include <optional>
#include <iostream>
// the Foo interface/base class
struct Foo
{
virtual ~Foo() = default;
virtual Foo* clone() const { return new Foo(*this); }
virtual void do_something() {
std::cout << "something Fooey\n";
}
};
// a service for managing Foo and classes derived from Foo
struct FooService
{
template<class Arg>
Foo* clone(Arg&& arg)
{
using d_type = std::decay_t<Arg>;
return new d_type(std::forward<Arg>(arg));
}
template<class Arg>
Foo* clone(Foo* arg)
{
return arg->clone();
}
Foo* release(Foo*& other) noexcept
{
auto tmp = other;
other = nullptr;
return tmp;
}
};
// implement the Foo interface in terms of a pimpl
template<class Holder>
struct BasicFoo
{
decltype(auto) do_something() {
return get().do_something();
}
private:
Foo& get() noexcept { return static_cast<Holder*>(this)->get_impl(); }
Foo const& get() const noexcept { return static_cast<Holder const*>(this)->get_impl(); }
};
// a type for holding anything derived from a Foo
// can be initialised by anything Foo-like and handles copy/move correctly
struct FooHolder : BasicFoo<FooHolder>
{
template
<
class Arg,
std::enable_if_t
<
std::is_base_of_v<Foo, std::decay_t<Arg>>
>* = nullptr
>
FooHolder(Arg&& arg)
: service_()
, ptr_(service_.clone(std::forward<Arg>(arg)))
{}
FooHolder(FooHolder const& other)
: service_()
, ptr_(other.ptr_->clone())
{
}
FooHolder(FooHolder && other) noexcept
: service_()
, ptr_(service_.release(other.ptr_))
{
}
FooHolder& operator=(FooHolder const& other)
{
auto tmp = other;
std::swap(ptr_, tmp.ptr_);
return *this;
}
FooHolder& operator=(FooHolder && other) noexcept
{
auto tmp = std::move(other);
std::swap(ptr_, tmp.ptr_);
return *this;
}
~FooHolder()
{
delete ptr_;
}
Foo& get_impl() noexcept { return *ptr_; }
Foo const& get_impl() const noexcept { return *ptr_; }
FooService service_;
Foo* ptr_;
};
// now we can supply as many overrides of Foo as we like
struct Bar : Foo
{
virtual Foo* clone() const { return FooService().clone(*this); }
virtual void do_something() {
std::cout << "something Barey\n";
}
};
int main()
{
std::optional<FooHolder> opt;
// note that we're initialising cleanly
opt = Bar {};
// and we don't expose the pointer so the user can't
// destroy the pimpl accidentally
opt.value().do_something();
}

Store different templated classes in one container without losing information about it's type

I'm currently working on a project where a client part of my application has to be able to create custom templated classes on the server. The server part has to keep track of these created classes and has to remember the types with which the classes has been instantiated. The problem is, that there are around 36 different class-template-combinations that are valid in my application. I'm currently struggling to keep track of these different types in a collection without losing information about my instances.
I'm currently using something like this:
#include <memory>
#include <type_traits>
#include <vector>
enum class data_type : std::uint8_t {
type_int = 1,
type_float,
type_double
};
enum class class_type : std:: uint8_t {
type_A = 1,
type_B
};
struct X {
virtual data_type get_data_type() = 0;
virtual class_type get_class_type() = 0;
};
template <typename T>
struct A : X {
data_type get_data_type() override
{
if (std::is_same<T, int>::value) {
return data_type::type_int;
} else if (std::is_same<T, float>::value) {
return data_type::type_float;
} else if (std::is_same<T, double>::value) {
return data_type::type_double;
} else {
/* ... */
}
}
class_type get_class_type() override
{
return class_type::type_A;
}
};
template <typename T>
struct B : X {
data_type get_data_type() override
{
if (std::is_same<T, int>::value) {
return data_type::type_int;
} else if (std::is_same<T, float>::value) {
return data_type::type_float;
} else if (std::is_same<T, double>::value) {
return data_type::type_double;
} else {
/* ... */
}
}
class_type get_class_type() override
{
return class_type::type_B;
}
};
struct Storage {
template <typename T, template <typename> class Class>
void create() {
Class<T>* t = new Class<T>();
_classes.push_back(std::unique_ptr<X>(t));
}
std::vector<std::unique_ptr<X>> _classes;
};
but I'm wondering if this is the way to go or if there is a more elegant way. Here I would have to always switch through the enums to get the full type out of my Storage class, something like:
switch(_classes.front()->get_class_type()) {
case class_type::type_A:
{
switch(_classes.front()->get_data_type()) {
case data_type::type_int:
{
/* I finally know that it is A<int> */
}
/* ... */
Thanks in advance.
You can consider using std::variant and the std::visit pattern
auto var = std::variant<int, float, double>{};
// assign var to value
std::visit([](auto& value) {
using Type = std::decay_t<decltype(value)>;
if constexpr (std::is_same<Type, int>{}) {
// is an int
} else if (std::is_same<Type, float>{}) {
// is float
} else if (std::is_same<Type, double>{}) {
// is double
}
}, var);
If the if constexpr looks ugly to you then you can substitute it with a handrolled visitor class as well.
class Visitor {
public:
void operator()(int& value) { ... }
void operator()(float& value) { ... }
void operator()(double& value) { ... }
};
auto var = std::variant<int, float, double>{};
// assign var to value
std::visit(Visitor{}, var);
As mentioned in the comments to the question, this is a viable approach that could help:
#include<vector>
#include<memory>
struct Counter {
static int next() {
static int v = 0;
return v++;
}
};
template<typename>
struct Type: Counter {
static int value() {
static const int v = Counter::next();
return v;
}
};
struct X {
virtual int get_data_type() = 0;
virtual int get_class_type() = 0;
};
template <typename T>
struct A : X {
int get_data_type() override {
return Type<T>::value();
}
int get_class_type() override {
return Type<A<T>>::value();
}
};
template <typename T>
struct B : X {
int get_data_type() override {
return Type<T>::value();
}
int get_class_type() override {
return Type<B<T>>::value();
}
};
struct Storage {
template <typename T, template <typename> class Class>
void create() {
Class<T>* t = new Class<T>();
_classes.push_back(std::unique_ptr<X>(t));
}
std::vector<std::unique_ptr<X>> _classes;
};
int main() {
Storage s;
s.create<int, A>();
if(Type<int>::value() == s._classes.front()->get_class_type()) {
//...
};
}
See it running on wandbox.

How to return different types from same function?

For study purpose, I make own Any type. I don't understand, how to return Type or Type * depending on the conditions. This is my sketch of class:
class any
{
public:
template<class T>
any(T & d)
{
data_container = new container_impl<T>(d);
}
template<class T>
any(T* d)
{
is_pointer = true;
data_container = new container_impl<T>(d);
}
bool check_is_pointer() const;
template<class T>
T a_cast(size_t id) const
{
auto real_id = data_container->get_id();
if (real_id != id)
{
//throw new exeption
}
return static_cast<container_impl<T>&>(*data_container).get_data();
}
private:
class abstract_container
{
public:
virtual ~abstract_container() { }
virtual size_t get_id() const = 0;
};
template<typename T>
class container_impl : public abstract_container
{
public:
container_impl(T const& value)
{
data = value;
id = type_id<T>();
pointer_data = nullptr;
}
container_impl(T const* value)
{
pointer_data = value;
id = type_id<T*>();
}
T get_data()
{
return data;
}
size_t get_id() const override
{
return id;
}
private:
T data;
T const* pointer_data;
size_t id;
};
abstract_container *data_container = nullptr;
bool is_pointer = false;
};
template<class T>
T any_cast(any const& obj)
{
size_t id = type_id<T>();
return obj.a_cast<T>(id);
}
template<class T>
T* any_cast(any const& obj)
{
size_t id = type_id<T>();
return obj.a_cast<T>(id);
}
As you can see, I would like to create two functions any_cast, which return different types.
You can't return different types. The way that Boost does it is actually with different signatures - note that the two different versions take different arguments. One takes a reference and the other takes a pointer.
template <typename T> // this one will try to cast your any down to T
T any_cast(any& ); // and return it if that succeeds, else throw
template <typename T> // this one will NOT throw, but will return
T* any_cast(any* ); // a pointer instead
Example usage of the two different functions:
any a = 5;
any_cast<double>(a); // throws
double* v = any_cast<double>(&a); // won't throw
assert(v == nullptr);
int* vi = any_cast<int>(&a); // succeeds
assert(*vi == 5);

Is it possible to implement events in C++?

I wanted to implement a C# event in C++ just to see if I could do it. I got stuck, I know the bottom is wrong but what I realize my biggest problem is...
How do I overload the () operator to be whatever is in T, in this case int func(float)? I can't? Can I? Can I implement a good alternative?
#include <deque>
using namespace std;
typedef int(*MyFunc)(float);
template<class T>
class MyEvent
{
deque<T> ls;
public:
MyEvent& operator +=(T t)
{
ls.push_back(t);
return *this;
}
};
static int test(float f){return (int)f; }
int main(){
MyEvent<MyFunc> e;
e += test;
}
If you can use Boost, consider using Boost.Signals2, which provides signals-slots/events/observers functionality. It's straightforward and easy to use and is quite flexible. Boost.Signals2 also allows you to register arbitrary callable objects (like functors or bound member functions), so it's more flexible, and it has a lot of functionality to help you manage object lifetimes correctly.
If you are trying to implement it yourself, you are on the right track. You have a problem, though: what, exactly, do you want to do with the values returned from each of the registered functions? You can only return one value from operator(), so you have to decide whether you want to return nothing, or one of the results, or somehow aggregate the results.
Assuming we want to ignore the results, it's quite straightforward to implement this, but it's a bit easier if you take each of the parameter types as a separate template type parameter (alternatively, you could use something like Boost.TypeTraits, which allows you to easily dissect a function type):
template <typename TArg0>
class MyEvent
{
typedef void(*FuncPtr)(TArg0);
typedef std::deque<FuncPtr> FuncPtrSeq;
FuncPtrSeq ls;
public:
MyEvent& operator +=(FuncPtr f)
{
ls.push_back(f);
return *this;
}
void operator()(TArg0 x)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(x);
}
};
This requires the registered function to have a void return type. To be able to accept functions with any return type, you can change FuncPtr to be
typedef std::function<void(TArg0)> FuncPtr;
(or use boost::function or std::tr1::function if you don't have the C++0x version available). If you do want to do something with the return values, you can take the return type as another template parameter to MyEvent. That should be relatively straightforward to do.
With the above implementation, the following should work:
void test(float) { }
int main()
{
MyEvent<float> e;
e += test;
e(42);
}
Another approach, which allows you to support different arities of events, would be to use a single type parameter for the function type and have several overloaded operator() overloads, each taking a different number of arguments. These overloads have to be templates, otherwise you'll get compilation errors for any overload not matching the actual arity of the event. Here's a workable example:
template <typename TFunc>
class MyEvent
{
typedef typename std::add_pointer<TFunc>::type FuncPtr;
typedef std::deque<FuncPtr> FuncPtrSeq;
FuncPtrSeq ls;
public:
MyEvent& operator +=(FuncPtr f)
{
ls.push_back(f);
return *this;
}
template <typename TArg0>
void operator()(TArg0 a1)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(a1);
}
template <typename TArg0, typename TArg1>
void operator()(const TArg0& a1, const TArg1& a2)
{
for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
(*it)(a1, a2);
}
};
(I've used std::add_pointer from C++0x here, but this type modifier can also be found in Boost and C++ TR1; it just makes it a little cleaner to use the function template since you can use a function type directly; you don't have to use a function pointer type.) Here's a usage example:
void test1(float) { }
void test2(float, float) { }
int main()
{
MyEvent<void(float)> e1;
e1 += test1;
e1(42);
MyEvent<void(float, float)> e2;
e2 += test2;
e2(42, 42);
}
You absolutely can. James McNellis has already linked to a complete solution, but for your toy example we can do the following:
#include <deque>
using namespace std;
typedef int(*MyFunc)(float);
template<typename F>
class MyEvent;
template<class R, class Arg>
class MyEvent<R(*)(Arg)>
{
typedef R (*FuncType)(Arg);
deque<FuncType> ls;
public:
MyEvent<FuncType>& operator+=(FuncType t)
{
ls.push_back(t);
return *this;
}
void operator()(Arg arg)
{
typename deque<FuncType>::iterator i = ls.begin();
typename deque<FuncType>::iterator e = ls.end();
for(; i != e; ++i) {
(*i)(arg);
}
}
};
static int test(float f){return (int)f; }
int main(){
MyEvent<MyFunc> e;
e += test;
e(2.0);
}
Here I've made use of partial specialization to tease apart the components of the function pointer type to discover the argument type. boost.signals does this and more, leveraging features such as type erasure, and traits to determine this information for non-function pointer typed callable objects.
For N arguments there are two approaches. The "easy' way, that was added for C++0x, is leveraging variadic templates and a few other features. However, we've been doing this since before that features was added, and I don't know which compilers if any, support variadic templates yet. So we can do it the hard way, which is, specialize again:
template<typename R, typename Arg0, typename Arg1>
class MyEvent<R(*)(Arg0, Arg1)>
{
typedef R (*FuncType)(Arg0, Arg1);
deque<FuncType> ls;
...
void operatror()(Arg0 a, Arg1)
{ ... }
MyEvent<FuncType>& operator+=(FuncType f)
{ ls.push_back(f); }
...
};
THis gets tedious of course which is why have libraries like boost.signals that have already banged it out (and those use macros, etc. to relieve some of the tedium).
To allow for a MyEvent<int, int> style syntax you can use a technique like the following
struct NullEvent;
template<typename A = NullEvent, typename B = NullEvent, typename C = NullEvent>
class HisEvent;
template<>
struct HisEvent<NullEvent,NullEvent,NullEvent>
{ void operator()() {} };
template<typename A>
struct HisEvent<A,NullEvent,NullEvent>
{ void operator()(A a) {} };
template<typename A, typename B>
struct HisEvent<A, B, NullEvent>
{
void operator()(A a, B b) {}
};
template<typename A, typename B, typename C>
struct HisEvent
{
void operator()(A a, B b, C c)
{}
};
static int test(float f){return (int)f; }
int main(){
MyEvent<MyFunc> e;
e += test;
e(2.0);
HisEvent<int> h;
HisEvent<int, int> h2;
}
The NullEvent type is used as a placeholder and we again use partial specialization to figure out the arity.
EDIT: Added thread safe implementation, based on this answer. Many fixes and performance improvements
This is my version, improving James McNellis' one by adding: operator-=, variadic template to support any ariety of the stored callable objects, convenience Bind(func, object) and Unbind(func, object) methods to easily bind objects and instance member functions, assignment operators and comparison with nullptr. I moved away from using std::add_pointer to just use std::function which in my attempts it's more flexible (accepts both lambdas and std::function). Also I moved to use std::vector for faster iteration and removed returning *this in the operators, since it doesn't look to be very safe/useful anyway. Still missing from C# semantics: C# events can't be cleared from outside the class where they are declared (would be easy to add this by state friendship to a templatized type).
It follows the code, feedback is welcome:
#pragma once
#include <typeinfo>
#include <functional>
#include <stdexcept>
#include <memory>
#include <atomic>
#include <cstring>
template <typename TFunc>
class Event;
template <class RetType, class... Args>
class Event<RetType(Args ...)> final
{
private:
typedef typename std::function<RetType(Args ...)> Closure;
struct ComparableClosure
{
Closure Callable;
void *Object;
uint8_t *Functor;
int FunctorSize;
ComparableClosure(const ComparableClosure &) = delete;
ComparableClosure() : Object(nullptr), Functor(nullptr), FunctorSize(0) { }
ComparableClosure(Closure &&closure) : Callable(std::move(closure)), Object(nullptr), Functor(nullptr), FunctorSize(0) { }
~ComparableClosure()
{
if (Functor != nullptr)
delete[] Functor;
}
ComparableClosure & operator=(const ComparableClosure &closure)
{
Callable = closure.Callable;
Object = closure.Object;
FunctorSize = closure.FunctorSize;
if (closure.FunctorSize == 0)
{
Functor = nullptr;
}
else
{
Functor = new uint8_t[closure.FunctorSize];
std::memcpy(Functor, closure.Functor, closure.FunctorSize);
}
return *this;
}
bool operator==(const ComparableClosure &closure)
{
if (Object == nullptr && closure.Object == nullptr)
{
return Callable.target_type() == closure.Callable.target_type();
}
else
{
return Object == closure.Object && FunctorSize == closure.FunctorSize
&& std::memcmp(Functor, closure.Functor, FunctorSize) == 0;
}
}
};
struct ClosureList
{
ComparableClosure *Closures;
int Count;
ClosureList(ComparableClosure *closures, int count)
{
Closures = closures;
Count = count;
}
~ClosureList()
{
delete[] Closures;
}
};
typedef std::shared_ptr<ClosureList> ClosureListPtr;
private:
ClosureListPtr m_events;
private:
bool addClosure(const ComparableClosure &closure)
{
auto events = std::atomic_load(&m_events);
int count;
ComparableClosure *closures;
if (events == nullptr)
{
count = 0;
closures = nullptr;
}
else
{
count = events->Count;
closures = events->Closures;
}
auto newCount = count + 1;
auto newClosures = new ComparableClosure[newCount];
if (count != 0)
{
for (int i = 0; i < count; i++)
newClosures[i] = closures[i];
}
newClosures[count] = closure;
auto newEvents = ClosureListPtr(new ClosureList(newClosures, newCount));
if (std::atomic_compare_exchange_weak(&m_events, &events, newEvents))
return true;
return false;
}
bool removeClosure(const ComparableClosure &closure)
{
auto events = std::atomic_load(&m_events);
if (events == nullptr)
return true;
int index = -1;
auto count = events->Count;
auto closures = events->Closures;
for (int i = 0; i < count; i++)
{
if (closures[i] == closure)
{
index = i;
break;
}
}
if (index == -1)
return true;
auto newCount = count - 1;
ClosureListPtr newEvents;
if (newCount == 0)
{
newEvents = nullptr;
}
else
{
auto newClosures = new ComparableClosure[newCount];
for (int i = 0; i < index; i++)
newClosures[i] = closures[i];
for (int i = index + 1; i < count; i++)
newClosures[i - 1] = closures[i];
newEvents = ClosureListPtr(new ClosureList(newClosures, newCount));
}
if (std::atomic_compare_exchange_weak(&m_events, &events, newEvents))
return true;
return false;
}
public:
Event()
{
std::atomic_store(&m_events, ClosureListPtr());
}
Event(const Event &event)
{
std::atomic_store(&m_events, std::atomic_load(&event.m_events));
}
~Event()
{
(*this) = nullptr;
}
void operator =(const Event &event)
{
std::atomic_store(&m_events, std::atomic_load(&event.m_events));
}
void operator=(nullptr_t nullpointer)
{
while (true)
{
auto events = std::atomic_load(&m_events);
if (!std::atomic_compare_exchange_weak(&m_events, &events, ClosureListPtr()))
continue;
break;
}
}
bool operator==(nullptr_t nullpointer)
{
auto events = std::atomic_load(&m_events);
return events == nullptr;
}
bool operator!=(nullptr_t nullpointer)
{
auto events = std::atomic_load(&m_events);
return events != nullptr;
}
void operator +=(Closure f)
{
ComparableClosure closure(std::move(f));
while (true)
{
if (addClosure(closure))
break;
}
}
void operator -=(Closure f)
{
ComparableClosure closure(std::move(f));
while (true)
{
if (removeClosure(closure))
break;
}
}
template <typename TObject>
void Bind(RetType(TObject::*function)(Args...), TObject *object)
{
ComparableClosure closure;
closure.Callable = [object, function](Args&&...args)
{
return (object->*function)(std::forward<Args>(args)...);
};
closure.FunctorSize = sizeof(function);
closure.Functor = new uint8_t[closure.FunctorSize];
std::memcpy(closure.Functor, (void*)&function, sizeof(function));
closure.Object = object;
while (true)
{
if (addClosure(closure))
break;
}
}
template <typename TObject>
void Unbind(RetType(TObject::*function)(Args...), TObject *object)
{
ComparableClosure closure;
closure.FunctorSize = sizeof(function);
closure.Functor = new uint8_t[closure.FunctorSize];
std::memcpy(closure.Functor, (void*)&function, sizeof(function));
closure.Object = object;
while (true)
{
if (removeClosure(closure))
break;
}
}
void operator()()
{
auto events = std::atomic_load(&m_events);
if (events == nullptr)
return;
auto count = events->Count;
auto closures = events->Closures;
for (int i = 0; i < count; i++)
closures[i].Callable();
}
template <typename TArg0, typename ...Args2>
void operator()(TArg0 a1, Args2... tail)
{
auto events = std::atomic_load(&m_events);
if (events == nullptr)
return;
auto count = events->Count;
auto closures = events->Closures;
for (int i = 0; i < count; i++)
closures[i].Callable(a1, tail...);
}
};
I tested it with this:
#include <iostream>
using namespace std;
class Test
{
public:
void foo() { cout << "Test::foo()" << endl; }
void foo1(int arg1, double arg2) { cout << "Test::foo1(" << arg1 << ", " << arg2 << ") " << endl; }
};
class Test2
{
public:
Event<void()> Event1;
Event<void(int, double)> Event2;
void foo() { cout << "Test2::foo()" << endl; }
Test2()
{
Event1.Bind(&Test2::foo, this);
}
void foo2()
{
Event1();
Event2(1, 2.2);
}
~Test2()
{
Event1.Unbind(&Test2::foo, this);
}
};
int main(int argc, char* argv[])
{
(void)argc;
(void)argv;
Test2 t2;
Test t1;
t2.Event1.Bind(&Test::foo, &t1);
t2.Event2 += [](int arg1, double arg2) { cout << "Lambda(" << arg1 << ", " << arg2 << ") " << endl; };
t2.Event2.Bind(&Test::foo1, &t1);
t2.Event2.Unbind(&Test::foo1, &t1);
function<void(int, double)> stdfunction = [](int arg1, double arg2) { cout << "stdfunction(" << arg1 << ", " << arg2 << ") " << endl; };
t2.Event2 += stdfunction;
t2.Event2 -= stdfunction;
t2.foo2();
t2.Event2 = nullptr;
}
That is possible, but not with your current design. The problem lies with the fact that the callback function signature is locked into your template argument. I don't think you should try to support this anyways, all callbacks in the same list should have the same signature, don't you think?