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);
}
Related
I have a std::array of class A, class A has a data member num_, I want to init a std::vector with all the num_ from the std::array is there a way to do it?. I know that I can loop over it and assign manually, I want to know if there is a way to do it like in my code snippet.
this is what I tried:
#include <iostream>
#include <vector>
#include <array>
class A{
public:
A():num_{0}{}
int num_;
};
int main()
{
std::array<A, 5> arr;
for(int i = 0; i<5; ++i)
{
arr[i].num_ = i;
}
std::vector<int> vec(arr.begin()->num_,arr.end()->num_);
for(auto& i : vec)
{
std::cout << i << " ";
}
return 0;
}
What you want can be done by using a projection iterator: an iterator implemented in terms of another iterator and a projection function - upon access the projection gets applied. For example, using std::ranges::views::transform() (of course, this requires C++20):
#include <ranges>
#include <iostream>
#include <vector>
#include <array>
class A{
public:
A():num_{0}{}
int num_;
};
int main()
{
std::array<A, 5> arr;
for(int i = 0; i<5; ++i)
{
arr[i].num_ = i;
}
auto view = std::ranges::views::transform(arr, [](auto& x){ return x.num_; });
std::vector<int> vec(view.begin(), view.end());
for(auto& i : vec)
{
std::cout << i << " ";
}
return 0;
}
With pre-C++20 the easiest approach to get a std::vector populated with the projected values is probably to not populate the std::vector during construction but rather to populate it via an std::back_inserter (as #john has pointed out in a comment):
std::transform(arr.begin(), arr.end(), std::back_inserter(vec),
[](A& a)->int& { return a.num_; });
To use construction it is necessary to build an iterator doing the relevant projection. It seems std::vector requires homogenous iterator for the begin and end which effectively means that some sort of view needs to be build which provides suitable begin and end iterator. Here is a basic version of this approach:
#include <iostream>
#include <optional>
#include <vector>
#include <array>
#include <type_traits>
class A{
public:
A():num_{0}{}
int num_;
};
template <typename FwdIt, typename Fun>
class projection_iterator
{
mutable FwdIt it;
mutable std::optional<Fun> fun;
public:
using value_type = std::decay_t<decltype(std::declval<Fun>()(*std::declval<FwdIt>()))>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = typename std::iterator_traits<FwdIt>::difference_type;
using iterator_category = std::forward_iterator_tag;
projection_iterator(): it(), fun(fun) {}
projection_iterator(FwdIt it, Fun fun): it(it), fun(fun) {}
reference operator*() const { return (*this->fun)(*this->it); }
pointer operator->() const { return &(*this->fun)(*this->it); }
projection_iterator& operator++() { ++this->it; return *this; }
projection_iterator operator++(int) { auto rc(*this); ++this->it; return rc; }
bool operator== (projection_iterator const& other) const { return this->it == other.it; }
bool operator!= (projection_iterator const& other) const { return this->it != other.it; }
bool operator== (FwdIt const& other) const { return this->it == other; }
bool operator!= (FwdIt const& other) const { return this->it != other; }
};
template <typename Range, typename Fun>
class project_range {
Range& range;
Fun fun;
template <typename FwdIt>
static auto make(FwdIt it, Fun fun) {
return projection_iterator<FwdIt, Fun>(it, fun);
}
public:
project_range(Range& range, Fun fun): range(range), fun(fun) {}
project_range(std::decay_t<Range>&&, Fun) = delete;
auto begin() { return make(range.begin(), fun); }
auto end() { return make(range.end(), fun); }
};
template <typename Range, typename Fun>
auto project(Range&& range, Fun fun) {
return project_range<Range, Fun>(range, fun);
}
int main()
{
std::array<A, 5> arr;
for(int i = 0; i<5; ++i)
{
arr[i].num_ = i;
}
auto p = project(arr, [](A& x)->int& { return x.num_; });
std::vector<int> vec(p.begin(), p.end());
for(auto& i : vec)
{
std::cout << i << " ";
}
return 0;
}
Unless absolutely necessary I wouldn't bother going down that route with pre-C++20 compilers.
I would like to have a class member variable to be able to switch in between items in a map so that when it is modified, the content of the map is also modified.
Is there any way other than to use a pointer to the content of the map ? Old code only needed only variable, now the new one needs to switch. If I change the variable type, then all functions using this member variable need to be changed. Not complicated, but I would find it ugly to have * in front of it everywhere...
A reference variable cannot be rebound, so how can I achieve this ?
class A
{
std::map<std::string,std::vector<int>> mMyMap;
std::vector<int>& mCurrentVector;
std::vector<int>* mCurrentVectorPointer;
std::vector<int> mDefaultVector;
void setCurrentVector(int iKey);
void addToCurrentVector(int iValue);
}
A::A():
mDefaultVector(std::vector<int>())
mCurrentVector(mDefaultVector)
{
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
mCurrentVectorPointer = &mMyMap[0];
}
A::setCurrentVector(std::string iKey)
{
if(mMyMap.find(iKey) != mMyMap.end())
{
mCurrentVector = mMyMap[iKey]; //can't change a reference...
mCurrentVectorPointer = &mMyMap[iKey]; //could use pointer, but
}
}
A::addToCurrentVector(int iValue)
{
mCurrentVector.push_back(iValue);
//or
(*mCurrentVectorPointer).push_back(iValue);
//
mCurrentVectorPointer->push_back(iValue);
}
void main()
{
A wClassA();
wClassA.setCurrentVector("key2");
wClassA.addToCurrentVector(3);
wClassA.setCurrentVector("key1");
wClassA.addToCurrentVector(4);
}
mMyMap["key1"] now contains 1,4
mMyMap["key2"] now contains 2,3
You can't reseat a reference once it has been assigned which means you are left with using your other option, a pointer.
As I understand it, you're refactoring some existing code which only used a single vector, whereas now you need a map of vectors.
You're trying to achieve this with minimal modifications, and keeping the interface to the vector the same.
An option would be to use a local reference, assigned from your pointer.
class A
{
using Vector = std::vector<int>;
public:
A()
{
map_["key1"] = std::vector<int>(1,1);
map_["key2"] = std::vector<int>(1,2);
curr_vec_ = &map_["key1"];
}
void setCurrentVector(const std::string& key)
{
if(map_.find(key) != map_.end())
{
curr_vec_ = &map_[key];
}
}
void addToCurrentVector(int val)
{
assert(curr_vec_);
Vector& curr_vec = *curr_vec_; // local reference
curr_vec.push_back(val);
curr_vec[0] = 2;
// etc
}
private:
std::map<std::string, Vector> map_;
Vector* curr_vec_ = nullptr;
}
You may write some wrapper:
#define Return(X) noexcept(noexcept(X)) -> decltype(X) { return X; }
template <typename U>
class MyVectorRef
{
private:
std::vector<U>* vec = nullptr;
public:
explicit MyVectorRef(std::vector<U>& v) : vec(&v) {}
void reset(std::vector<U>& v) {vec = &v;}
// vector interface
auto at(std::size_t i) const Return(vec->at(i))
auto at(std::size_t i) Return(vec->at(i))
auto operator [](std::size_t i) const Return(vec->operator[](i))
auto operator [](std::size_t i) Return(vec->operator[](i))
template <typename ... Ts> auto assign(Ts&&... ts) Return(vec->assign(std::forward<Ts>(ts)...))
auto assign( std::initializer_list<U> ilist ) Return(vec->assign(ilist))
template <typename T> auto push_back(T&& t) const Return(vec->push_back(std::forward<T>(t)))
template <typename T> auto emplace_back(T&& t) const Return(vec->emplace_back(std::forward<T>(t)))
auto begin() const Return(vec->begin())
auto begin() Return(vec->begin())
auto end() const Return(vec->end())
auto end() Return(vec->end())
auto cbegin() const Return(vec->cbegin())
auto cend() const Return(vec->cend())
// ...
};
and then, use it:
class A
{
public:
A() : mCurrentVector(mDefaultVector) {
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
}
std::map<std::string, std::vector<int>> mMyMap;
std::vector<int> mDefaultVector;
MyVectorRef<int> mCurrentVector;
void setCurrentVector(std::string iKey)
{
auto it = mMyMap.find(iKey);
if (it != mMyMap.end())
{
mCurrentVector.reset(it->second);
}
}
void addToCurrentVector(int iValue)
{
mCurrentVector.push_back(iValue);
}
};
But I think it would be simpler to just create a getter in A and use directly a pointer:
class A
{
public:
A() : mCurrentVector(&mDefaultVector) {
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
}
std::map<std::string, std::vector<int>> mMyMap;
std::vector<int> mDefaultVector;
std::vector<int>* mCurrentVector;
std::vector<int>& GeCurrentVector() { return *mCurrentVector; }
void setCurrentVector(std::string iKey)
{
auto it = mMyMap.find(iKey);
if (it != mMyMap.end())
{
mCurrentVector = &it->second;
}
}
void addToCurrentVector(int iValue)
{
GeCurrentVector().push_back(iValue);
}
};
I have some duplicated code in which I read from two streams,
{
std::ifstream ifs("A.dat");
... code ...
}
{
std::ifstream ifs("B.dat");
... same code ...
}
I wanted to unify both in one loop.
The first reaction is to do this:
for(auto ifs : {ifstream("A.dat"), ifstream("B.dat")})
{
... code ...
}
However it doesn't compile because the type is not copyable, so I tried this:
for(auto& ifs : {ifstream("A.dat"), ifstream("B.dat")})
{
... code ...
}
that doesn't work because ifs inside the loop is const. (a const ifstream cannot be used.)
This didn't work either, I think for the same reason:
for(auto&& ifs : {ifstream("A.dat"), ifstream("B.dat")})
At the end of course I ended up doing this.
#include<iostream>
int main(){
for(auto& name : {"A.dat", "B.dat"})
{
std::ifstream ifs(name);
... code ...
}
But I am still curious if it is possible to have range for-loop directly with a type like std::ifstream?
std::ifstream streams[2];
streams[0].open("A.dat");
streams[1].open("B.dat");
for (auto &stream:streams)
{
// ...
}
In c++, everything is possible.
Imagine being able to iterator a collection of any kind of stream, like this:
int main()
{
std::string buffer;
for (auto& stream : streams(std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt")))
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
}
}
Well now you can. Behold: a polymorphic temporary container and iterator for any istream.
A similar technique will work for any arbitrary collection of objects that share a common polymorphic interface.
#include <iostream>
#include <fstream>
#include <sstream>
#include <utility>
#include <tuple>
#include <array>
namespace detail {
template<class Interface>
struct iface_iter
{
using value_type = Interface;
using reference = Interface&;
using internal_p = value_type * const *;
iface_iter(internal_p pp) : _pp(pp) {}
reference operator*() const {
return **_pp;
}
iface_iter& operator++() {
++_pp;
return *this;
}
bool operator==(const iface_iter& r) const {
return _pp == r._pp;
}
bool operator!=(const iface_iter& r) const {
return !(*this == r);
}
internal_p _pp;
};
template<class CommonType, class...Streams>
struct common_sequence
{
using common_type = CommonType;
using iterator = iface_iter<common_type>;
constexpr static std::size_t size() { return sizeof...(Streams); }
using storage_type = std::tuple<Streams...>;
using pointer_array = std::array<common_type*, size()>;
common_sequence(Streams...streams)
: _storage(std::move(streams)...)
, _pointers(build_pointers(std::make_index_sequence<size()>(), _storage))
{}
common_sequence(common_sequence&& r)
: _storage(std::move(r._storage))
, _pointers(build_pointers(std::make_index_sequence<size()>(), _storage))
{
}
common_sequence& operator=(common_sequence&& r)
{
_storage = std::move(r._storage);
_pointers = build_pointers(std::make_index_sequence<size()>(), _storage);
}
template<std::size_t I>
using stream_type = std::tuple_element_t<I, storage_type>;
template<std::size_t...Is>
static constexpr
pointer_array build_pointers(std::index_sequence<Is...>,
std::tuple<Streams...>& tup)
{
return pointer_array {
static_cast<common_type*>(&static_cast<stream_type<Is>&>(std::get<Is>(tup)))...
};
}
iterator begin() const {
return { _pointers.data() };
}
iterator end() const {
return { _pointers.data() + size() };
}
mutable storage_type _storage;
pointer_array _pointers;
};
}
template<class CommonBase, class...Things>
auto make_common_sequence(Things&&...ts)
{
return detail::common_sequence<CommonBase, std::decay_t<Things>...>(std::move(ts)...);
}
template<class...Streams>
auto streams(Streams&&...strs)
{
return make_common_sequence<std::istream>(std::move(strs)...);
}
struct base
{
virtual void foo() = 0;
};
struct d1 : base
{
void foo() override { std::cout << "d1::foo" << std::endl; }
};
struct d2 : base
{
void foo() override { std::cout << "d2::foo" << std::endl; }
};
template<class...Ts>
auto bases(Ts&&...ts)
{
return make_common_sequence<base>(std::move(ts)...);
}
int main()
{
std::string buffer;
for (auto& stream : streams(std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt")))
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
}
for (auto& f : bases(d1(), d2(), d1(), d2()))
{
f.foo();
}
return 0;
}
expected output:
hello
world
funky
chicken
... plus whatever is in foo.txt ...
d1::foo
d2::foo
d1::foo
d2::foo
Of course, if we don't require polymorphism, a simple template variadic argument iterator will suffice:
template<class F, class...Things>
void apply_to_all(F f, Things... things)
{
using expand = int[];
void(expand{ 0,
(f(things), 0)...
});
}
int main()
{
std::string buffer;
apply_to_all([&](auto& stream)
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
},
std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt"));
}
Inspired by #SamVarshavchik's answer I found that this works:
for (auto& ifs: std::array<std::ifstream, 2>{std::ifstream("A.dat"), std::ifstream("B.dat")} )
{
// ...
}
With std::make_array from TS2 (http://en.cppreference.com/w/cpp/experimental/make_array) this will work too:
for (auto& ifs: std::make_array(std::ifstream("A.dat"), std::ifstream("B.dat")) )
{
// ...
}
And with a further hack
template < class TT, class... Types>
constexpr std::array<TT, sizeof...(Types)> make_array_of(Types&&... t) {
return {TT(std::forward<Types>(t))... };
}
I can do
for (auto& ifs: make_array_of<std::ifstream>("A.dat", "B.dat") ){
...
}
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
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?