C++ pairs and pointers - c++

I don't know what can I do to make this work in C++.
The intend is:
pair<int, int> foo() {
if(cond) {
return std::make_pair(1,2);
}
return NULL; //error: no viable conversion from 'long' to 'pair<int, int>
}
void boo() {
pair<int, int> p = foo();
if (p == NULL) { //error: comparison between NULL and non-pointer ('int, int' and NULL)
// doA
} else {
int a = p.first;
int b = p.second;
// doB
}
}
Since I cannot use return NULL in C++, here is my 2nd try:
pair<int, int>* foo() {
if(cond) {
return &std::make_pair(1,2); //error: returning address of local temporary object)
}
return nullptr;
}
void boo() {
pair<int, int>* p = foo();
if (p == nullptr) {
// doA
} else {
int a = p->first;
int b = p->second;
// doB
}
}
What is the correct way to be able to return a pair and a null value.

You should use an exception:
std::pair<int, int> foo() {
if(cond) {
return std::make_pair(1,2);
}
throw std::logic_error("insert error here");
}
and in your boo function:
try {
std::pair<int, int> p = foo();
int a = p.first;
int b = p.second;
} catch (std::logic_error const& e) {
// do something
}
And here's the live example.
In alternative you can use std::optional (since C++14) or boost::optional:
std::optional<std::pair<int, int>> foo() {
if(cond) {
return std::make_pair(1,2);
}
return {};
}
std::optional<std::pair<int, int>> p = foo();
if (p) {
int a = p->first;
int b = p->second;
} else {
// do something
}
And here's a the working example with the boost version.

Try passing in a pair to foo() by reference, then returning a bool from that function indicating success or failure. Like so:
bool foo(pair<int, int>& myPair) {
if(cond) {
myPair = std::make_pair(1,2);
return true;
}
return false;
}
void boo() {
pair<int, int> myPair;
if (!foo(myPair)) {
// doA
} else {
int a = myPair.first;
int b = myPair.second;
// doB
}
}
Edit: Depending on what you're doing, you should just kill foo() if possible and evaluate cond within boo()
void boo() {
pair<int, int> myPair;
if (!cond) {
// doA
} else {
myPair = std::make_pair(1,2);
int a = myPair.first;
int b = myPair.second;
// doB
}
}

Perhaps you're looking for a Nullable type. There's no reason to use dynamic memory. This approach is completely bombastic and unnecessary, but it most fits what you were trying to 'achieve' with your original code.
#include <iostream>
#include <map>
template <typename T, typename U>
struct NullablePair
{
std::pair<T, U> pair;
T first = pair.first;
U second = pair.second;
NullablePair(const std::pair<T, U>& other) : pair(other) { }
NullablePair() { }
operator std::nullptr_t() const {
return nullptr;
}
};
template <typename T, typename U>
NullablePair<T, U> foo(bool cond) {
if(cond) {
return NullablePair<T, U>(std::make_pair(1,2));
}
return NullablePair<T, U>();
}
void boo() {
NullablePair<int, int> p = foo<int, int>(false);
if (p == nullptr) {
std::cout << "Nullptr.\n";
} else {
int a = p.first;
int b = p.second;
}
}
int main()
{
boo();
}

If you want to maintain the function signature, then you can use pair. So in your return statement in the function foo(), you can return std::make_pair(NULL, NULL).
pair<int, int> foo()
{
// Initialize cond
if(cond)
return std::make_pair(1,2);
return std::make_pair(NULL, NULL);
}
You can then check if p.first and p.second equal NULL in boo() function

Related

How to properly implement -> and (*). so that they behave like -> and (*). in an iterator

I'd like to implement the * and -> operators for the iterator of a custom-made container. My code doesn't compile. Below a "Minimal Non-working example", which shows how it works with std::map but not in my code.
#include <vector>
#include <map>
#include <iostream>
struct thing {
float f[1];
typedef std::pair<int,float> key_data;
struct iterator {
int pos;
const float *f;
key_data operator*() const { return key_data(pos,f[pos]); }
key_data *operator->() const { return &key_data(pos,f[pos]); }
};
iterator begin() const { return {.f = f, .pos = 0}; }
};
template<typename T> void test(T iter) {
(*iter).second = 1.0; std::cout << (*iter).second;
iter->second = 2.0; std::cout << iter->second;
}
int main() {
std::map<int,float> fmap; fmap[0] = 0.0;
test(fmap.begin());
std::cout << fmap[0];
thing f;
test(f.begin());
std::cout << f.f[0];
}
I would like this to compile :), and print 122122. The error messages on compilation are:
access.cc:13:43: error: taking the address of a temporary object of type 'key_data'
(aka 'pair<int, float>') [-Waddress-of-temporary]
key_data *operator->() const { return &key_data(pos,f[pos]); }
access.cc:20:18: error: expression is not assignable
(*iter).second = 1.0; std::cout << (*iter).second;
For the first: fair enough, std::pair<> creates a temporary which can't be returned by reference; but how does the Standard library do it to allow the usual -> syntax?
For the second: probably again I'm trying to assign into a temporary, but I can't guess what the right syntax is.
Simpler is to have directly correct type in iterator:
struct thing {
float f[1];
using key_data = std::pair<int,float&>;
struct iterator {
key_data data;
const key_data& operator*() { return data; }
key_data *operator->() { return &data; }
};
iterator begin() { return {{0, f[0] }}; }
};
Demo
Another solution is to have wrapper, something like:
struct pair_wrapper
{
int &first;
float& second;
pair_wrapper* operator->() { return this; }
};
struct thing {
float f[1];
typedef std::pair<int,float> key_data;
struct iterator {
int pos;
float *f;
pair_wrapper operator*() { return pair_wrapper{pos,f[pos]}; }
pair_wrapper operator->() { return pair_wrapper{pos,f[pos]}; }
};
iterator begin() { return {0, f}; }
};
Demo
It uses the "magic" chaining of operator->.

Using same template for regular pointer and shared pointer

This one does not work. I wanted to reuse the template that I made for regular pointer. How can I use the same template for std::shared_ptr
class Base
{
public:
int getVal() { return 0; }
};
template <class Type>
bool writeRecordForSet(std::vector<Type> entityPtr)
{
if (entityPtr.size() == 0) return true;
//...
for (auto iter = entityPtr.begin(); iter != entityPtr.end(); iter++) {
Type enPtr = *iter;
int myval = enPtr->getVal();
}
return true;
}
int main()
{
std::vector<std::shared_ptr<Base>> vec_shared;
std::vector<int*> vec_intp;
std::vector<std::unique_ptr<Base>> vec_unique_ptr;
writeRecordForSet(vec_shared);
writeRecordForSet(vec_intp);
writeRecordForSet(vec_unique_ptr);
}
You cannot copy a std::unique_ptr<>, nor a std::vector<std::unique_ptr<>>, so don't try that, but take the argument by reference (which you should to anyway; also use a range-based for loop for clarity)
template <class Type>
bool writeRecordForSet(std::vector<Type> const&entityPtr)
{
if (entityPtr.size() == 0) return true;
for(const auto&enPtr : entityPtr) {
auto myval = enPtr->getVal();
/* ... */
}
return true;
}
Of course, this will fail to compile if called for a Type that does not allow for Type::getVal() (such as your vector<int*> example). If you want your function to work for Types that don't have such a getter, you can use a getter adaptation, i.e.
template<typename T>
inline auto getVal(T const&x) { return T::getVal(); }
inline int getVal(int*x) { return *x; }
Problem 1
bool writeRecordForSet(std::vector<Type> entityPtr) { ... }
is a problem when the argument is of type std::unique_ptr since they cannot be copy constructed.
Change that to use a reference:
bool writeRecordForSet(std::vector<Type>& entityPtr) { ... }
or
bool writeRecordForSet(std::vector<Type> const& entityPtr) { ... }
Problem 2
In the function, you are assuming that the core object is of type Base.
for (auto iter = entityPtr.begin(); iter != entityPtr.end(); iter++) {
Type enPtr = *iter;
// Assuming that enPtr is a Base*, or shared_ptr<Base>, or unique_ptr<Base>
int myval = enPtr->getVal();
}
If you use
std::vector<Base*> vec_intp;
instead of
std::vector<int*> vec_intp;
it will work.
You can add getVal adaptor function to make it work and then write multiple overloads for your int*, shared_ptr<Base>, unique_ptr<Base>.
For example, getVal for int* could be as simple as:
int getVal(int*& p)
{
return *p;
}
And then your writeRecordForSet should call getVal to get int value.
Full example:
#include <vector>
#include <memory>
using namespace std;
class Base
{
public:
int getVal() { return 0; }
};
int getVal(shared_ptr<Base>& p)
{
return p->getVal();
}
int getVal(unique_ptr<Base>& p)
{
return p->getVal();
}
int getVal(int*& p)
{
return *p;
}
template <class Type>
bool writeRecordForSet(std::vector<Type>& entityPtr)
{
if (entityPtr.size() == 0)
return true;
//...
for (auto iter = entityPtr.begin(); iter != entityPtr.end(); iter++) {
int myval = getVal(*iter);
}
return true;
}
int main()
{
std::vector<std::shared_ptr<Base>> vec_shared;
std::vector<int*> vec_intp;
std::vector<std::unique_ptr<Base>> vec_unique_ptr;
writeRecordForSet(vec_shared);
writeRecordForSet(vec_intp);
writeRecordForSet(vec_unique_ptr);
}

How to avoid duplicate code including std::map and std::mutex

I have a class including a map, and a mutex. In every member function the mutex protects the map from multiple thread accessing the object of this class, for ex:
class bar
{
public:
void hello() {}
void set_something(int x) {}
int get_something(int x, int y) { return x + y; }
};
class foo
{
public:
foo()
{
m_map[0];
m_map[1];
m_map[2];
}
void hello(unsigned int index)
{
std::lock_guard<std::mutex> lock(m_mut);
const auto iter = m_map.find(index);
if (iter != m_map.end())
iter->second.hello();
}
void set_something(unsigned int index, int x)
{
std::lock_guard<std::mutex> lock(m_mut);
const auto iter = m_map.find(index);
if (iter != m_map.end())
iter->second.set_something(x);
}
int get_something(unsigned int index, int x, int y)
{
std::lock_guard<std::mutex> lock(m_mut);
const auto iter = m_map.find(index);
if (iter != m_map.end())
return iter->second.get_something(x, y);
return 0;
}
private:
std::mutex m_mut;
std::map<unsigned int, bar> m_map;
};
Is there an elegant method to avoid duplicate code?
You could use a proxy and RAII like this :
#include <iostream>
#include <mutex>
#include <map>
template < typename F, typename S>
struct mutex_map {
std::recursive_mutex m_;
std::map<F,S> map_;
struct Proxy {
Proxy( std::map<F,S> & map, std::recursive_mutex &m ) : map_(&map), lock_(m) {
std::cout << "lock\n";
}
~Proxy() { std::cout << "unlock\n"; }
std::map<F,S>* map_;
std::unique_lock<std::recursive_mutex> lock_;
std::map<F,S>* operator->() { return map_; }
};
Proxy operator->() {
return { map_, m_ };
}
};
int main() {
mutex_map<int, int> mm;
mm->emplace(1, 3);
std::cout << (mm->find(1) == mm->end()) << "\n";
std::cout << (mm->find(2) == mm->end()) << "\n";
}
You can move common part into "do_something" and pass functor into it:
...
void do_something(const std::function<void(bar&)>& func)
{
std::lock_guard<std::mutex> lock(m_mut);
const auto iter = m_map.find(index);
if (iter != m_map.end())
func(std::ref(*iter));
}
int get_something(unsigned int index, int x, int y)
{
do_something(std::bind(&bar::get_something, std::placeholders::_1, index, x, y));
}
...
One question - do you need to protect with mutex only std::map or std::map and its elements? You may think about granularity

overload array operator with templates and pointers

The following code compiles (without warnings) on both clang++-2.9 and g++-4.6. However, the g++ binary Seg Faults, while the clang++ binary runs as intended.
What is the proper way to access template class data members through pointers when overloading []?
Here's the code:
#include <iostream>
template <typename T>
class A {
private:
T val1;
T val2;
public:
T& getVal1() { return val1; }
void setVal1(T aVal) { val1 = aVal; }
T& getVal2() { return val2; }
void setVal2(T aVal) { val2 = aVal; }
};
template <typename T>
class B {
private:
A<T>* aPtr;
public:
A<T>* getAPtr() { return aPtr; }
T& operator[](const int& key) {
if(key == 0) { T& res = getAPtr()->getVal1();
return res; }
else { T& res = getAPtr()->getVal2();
return res; }
}
};
int main()
{
B<int> foo;
foo[0] = 1;
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
You are returning a reference to a local variable (res). The reference won't be valid after returning from operator[]. It could be overwritten by other stuff. What really happens is Undefined: that is why compilers are allowed to eat your children or grow a moustache: Undefined Behaviour
You probably want to return by value.
Edit
Since you have a setter, you don't need the reference: See the solution live at http://ideone.com/oxslQ
Note: there was another problem with aPtr not being initialized. I proposed a simple constructor for that. _You might want to initialize this from elsewhere OR you need
assignment and copy constructors
or use a shared_ptr for aPtr
.
#include <iostream>
template <typename T>
class A
{
private:
T val1;
T val2;
public:
T getVal1()
{
return val1;
}
void setVal1(T aVal)
{
val1 = aVal;
}
T getVal2()
{
return val2;
}
void setVal2(T aVal)
{
val2 = aVal;
}
};
template <typename T>
class B
{
private:
A<T>* aPtr;
B(const B&); // TODO , disallow for now
B& operator=(const B&); // TODO , disallow for now
public:
B() : aPtr(new A<T>()) {}
~B() { delete aPtr; }
A<T>* getAPtr()
{
return aPtr;
}
T operator[](const int& key)
{
if(key == 0)
{
T res = getAPtr()->getVal1();
return res;
}
else
{
T res = getAPtr()->getVal2();
return res;
}
}
};
int main()
{
B<int> foo;
foo.getAPtr()->setVal1(1);
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
If you want to return by ref, then your A::getValX() functions should also return by ref, and your res variable inside B::operator should also be T& instead of T:
#include <iostream>
template <typename T>
class A {
private:
T val1;
T val2;
public:
T& getVal1() { return val1; }
void setVal1(T aVal) { val1 = aVal; }
T& getVal2() { return val2; }
void setVal2(T aVal) { val2 = aVal; }
};
template <typename T>
class B {
private:
A<T>* aPtr;
public:
A<T>* getAPtr() { return aPtr; }
T& operator[](const int& key) {
if(key == 0) { T& res = getAPtr()->getVal1();
return res; }
else { T& res = getAPtr()->getVal2();
return res; }
}
};
int main()
{
B<int> foo;
foo[0] = 1;
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
(Note that it will still crash at runtime, since aPtr isn't initialized anywhere.)
Your original code returns a reference to the local variable res, not to A::val1 / A::val2 as you probably intended. If res is a non-reference variable, then it will be a simple copy of the val1 / val2 value, that is only valid for inside the scope (in this case the function) where it was declared. So you need a reference here.

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?