I have a bunch of derived classes stored as shared pointers, I was wondering if there is any way of getting a weak_ptr to the object from inside the object?
I've tried using the shared_from_this() function but the problem is that since it's a derived class, when I make the base class inherit from enable_shared_from_this, when the derived class calls shared_from_this() it gets a shared_ptr of the base class not the derived class which I can't turn into a shared_ptr of the derived class
Any suggestions?
Usign CRTP you can achieve it:
#include <memory>
template<typename T>
struct B: std::enable_shared_from_this<T> {};
struct D: B<D> {};
int main() {
std::shared_ptr<B<D>> b = std::make_shared<D>();
std::shared_ptr<D> d = b->shared_from_this();
std::weak_ptr<D> w = b->shared_from_this();
}
If you want to have a common, non-template base class, you can rely on techniques like the double dispatching, as in the following example:
#include <memory>
#include <iostream>
struct D1;
struct D2;
struct S {
void doSomething(std::weak_ptr<D1> weak) { std::cout << "D1" << std::endl; }
void doSomething(std::weak_ptr<D2> weak) { std::cout << "D2" << std::endl; }
};
struct B: std::enable_shared_from_this<B> {
virtual void dispatch(S &) = 0;
};
template<typename T>
struct M: B {
void dispatch(S &s) override {
auto ptr = std::static_pointer_cast<T>(shared_from_this());
s.doSomething(ptr);
}
};
struct D1: M<D1> {};
struct D2: M<D2> {};
int main() {
std::shared_ptr<B> b = std::make_shared<D1>();
S s;
b->dispatch(s);
}
As #Torbjörn said, using the dynamic_pointer_cast<Derived>(base_ptr) fixed this problem as it allowed me to convert shared_ptr's down in inheritance, something that isn't directly allowed.
Related
Here is the specifics:
Consider a simple constructor in a class with two input arguments,
concreteclass(_1, _2).
I have a map for this instantiation, map <string, concreteclassType>.
Also, these classes work with different datatypes concreteclass<double>(_1,_2) is different from concreteclass<int>(_1,_2).
Now that my problem is described above here is what I try to do using boost::factory pattern, classes defined in a string map and datatypes defined in an enum.
First, there is a simple way to demonstrate how boost factory pattern can be used with constructor arguments, the following nicely code works:
// Factory which takes two arguments
struct base {
base(int alpha) : alpha(alpha) {}
virtual ~base() = default;
virtual void print() const = 0;
int alpha;
};
struct derived : public base {
derived(int alpha, int beta) : base(alpha), beta(beta) {}
void print() const override {
std::cout << alpha << " " << beta << std::endl;
}
int beta;
};
void TestBoostFactoryWithTwoArgs()
{
// Constructor factory with two input args
{
std::map<std::string, boost::function<base* (int&, int&)>> factories;
factories["derived"] = boost::bind(boost::factory<derived*>(), _1, _2);
int x = 42;
int y = 51;
std::unique_ptr<base> b{ factories.at("derived")(x,y) };
b->print();
}
// Factory with two initialized inputs args - binding of values not at run time
{
std::map<std::string, boost::function<base* ()>> factories;
factories["derived"] = boost::bind(boost::factory<derived*>(), 42, 51);
std::unique_ptr<base> b{ factories.at("derived")() };
b->print();
}
}
Now consider my code - SimpleClasses.h:
// Dummy base class - non template
class IBaseClass
{
public:
};
// Templatized Derived Base class
template <typename T>
class ConcreteClass : public IBaseClass
{
private:
std::shared_ptr<IBaseClass> m_leftArgument;
std::shared_ptr<IBaseClass> m_leftArgument;
public:
ConcreteClass(std::unique_ptr<IBaseClass>& leftArgument, std::unique_ptr<IBaseClass>& rightArgument)
{
m_leftArgument = leftArgument;
m_rightArgument = rightArgument;
};
virtual T DoSomething()
{
cout << "I did something in Concrete Base Class" << endl;
return T();
}; // This is the main reason for creating T
};
template <typename T>
class ConcreteClassA : ConcreteClass
{
};
template <typename T>
class ConcreteClassB : ConcreteClass
{
};
template <typename T>
class ConcreteClassC : ConcreteClass
{
};
Another File, ClassFactory.h :
#pragma once
#include "SimpleClasses.h"
#include <memory>
#include <map>
#include <boost/functional/overloaded_function.hpp>
#include <boost/functional/factory.hpp>
using namespace std;
// Add More class Keys here
namespace MyClassesNamespace { // These are all string keys
static const string CLASS_A = "specialclassA";
static const string CLASS_B = "specialclassB";
static const string CLASS_C = "specialclassC";
};
enum EMyDataTypes
{
INT8,
FLOAT8,
FLOAT16,
};
// This type def we keep for non templatized base class constructor
typedef boost::function<IBaseClass*(std::unique_ptr<IBaseClass>&, std::unique_ptr<IBaseClass>&)> IBaseClassConstructorFunc_factory;
// Dummy base factory - no template
class UBaseClassTemplateFactory
{
public:
};
template<typename T>
class UClassFactoryTemplate : public UBaseClassTemplateFactory
{
private:
static std::map<string, IBaseClassConstructorFunc_factory> ClassFactoryTemplateMap; // Unique Classes only
public:
UClassFactoryTemplate();
__forceinline static UClassFactoryTemplate*Get()
{
static UClassFactoryTemplate<T> SingletonInstance;
return &SingletonInstance;
}
static std::unique_ptr<IBaseClass<T>> CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument);
};
// This type def we keep for non templatized base class
typedef boost::function<UBaseClassTemplateFactory*()> ClassFactoryTemplate_factory;
/* This is the instance class that resolves the classes as well as the concrete datatype to be used in UClassFactoryTemplate*/
class UClassFactory
{
private:
UClassFactory();
static std::map<EMyDataTypes, ClassFactoryTemplate_factory> ClassDataTypeTemplateFactoryMap;
public:
__forceinline static UClassFactory *Get()
{
static UClassFactory SingletonInstance;
return &SingletonInstance;
}
static std::unique_ptr<IBaseClass> CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal = EMyDataTypes::INT8);
};
Finally, in ClassFactory.cpp
#include "ClassFactory.h"
#include <boost/bind.hpp>
/*static, but non-const data members should be defined outside of the class definition
*and inside the namespace enclosing the class. The usual practice is to define it in
*the translation unit (*.cpp) because it is considered to be an implementation detail.
*Only static and const integral types can be declared and defined at the same time (inside class definition):*/
template<typename T>
std::map<string, IBaseClassConstructorFunc_factory> UClassFactoryTemplate<T>::ClassFactoryTemplateMap;
std::map<EMyDataTypes, ClassFactoryTemplate_factory> UClassFactory::ClassDataTypeTemplateFactoryMap;
template<typename T>
inline UClassFactoryTemplate<T>::UClassFactoryTemplate()
{
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_A] = boost::bind(boost::factory<ConcreteClassA<T>*>(), _1, _2);
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_B] = boost::bind(boost::factory<ConcreteClassB<T>*>(), _1, _2);
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_C] = boost::bind(boost::factory<ConcreteClassC<T>*>(), _1, _2);
}
template<typename T>
std::unique_ptr<IBaseClass<T>> UClassFactoryTemplate<T>::CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument)
{
std::unique_ptr<IBaseClass<T>> someTemplatizedDataTypeInstance{ ClassFactoryTemplateMap.at(ClassString) (LeftArgument,RightArgument) };
return someTemplatizedDataTypeInstance;
}
UClassFactory::UClassFactory()
{
ClassDataTypeTemplateFactoryMap[EMyDataTypes::INT8] = boost::bind(boost::factory<UClassFactoryTemplate<int>*>());
ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT8] = boost::bind(boost::factory<UClassFactoryTemplate<float>*>());
ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT16] = boost::bind(boost::factory<UClassFactoryTemplate<double>*>());
}
std::unique_ptr<IBaseClass> UClassFactory::CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal)
{
std::unique_ptr<UBaseClassTemplateFactory> BaseOperatorTempFactory{ ClassDataTypeTemplateFactoryMap.at(someEnumVal) };
return BaseOperatorTempFactory->Get()::CreateClassTemplatized(ClassString, LeftVal, RightVal);
}
The question now is, the above code does not even compile let alone run, it says abstract class cannot be instantiated for the templatized map. I just want the UClassFactory to return me correct instantiated class like A,B,C based on a string map with correct datatypes based on an enum. How do I achieve this combination? I wonder what is the correct syntax? Or is my approach inherently flawed? Or there is a nice way to instantiate classes with factory pattern and different datatypes? Please let me know any suggestions/ comments.
Thanks
Alam
Is there a way to write a copy-constructor for a class (say, Copyable, that holds a std::unique_ptr to a Base class (but really is storing Derived objects.
A quick test shows the expected slicing occurs, because Copyable doesn't know the real type it's holding. So I suppose a clone method is needed, but I'm wondering if there is a way to let the compiler handle this in some better way?
The slicing code:
#include <algorithm>
#include <iostream>
#include <memory>
struct Base
{
Base(int i = 0) : i(i) {}
virtual ~Base() = default;
int i;
virtual int f() { return i; }
};
struct Derived : Base
{
Derived() = default;
virtual int f() override { return 42; }
};
struct Copyable
{
Copyable(std::unique_ptr<Base>&& base) : data(std::move(base)) {}
Copyable(const Copyable& other)
{
data = std::make_unique<Base>(*other.data);
}
std::unique_ptr<Base> data;
};
int main()
{
Copyable c(std::make_unique<Derived>());
Copyable c_copy = c;
std::cout << c_copy.data->f() << '\n';
}
The clone code:
#include <algorithm>
#include <iostream>
#include <memory>
struct Base
{
Base(int i = 0) : i(i) {}
virtual ~Base() = default;
int i;
virtual int f() { return i; }
virtual Base* clone() { return new Base(i); }
};
struct Derived : Base
{
Derived() = default;
virtual int f() override { return 42; }
virtual Derived* clone() override { return new Derived(); }
};
struct Copyable
{
Copyable(std::unique_ptr<Base>&& base) : data(std::move(base)) {}
Copyable(const Copyable& other)
{
data.reset(other.data->clone());
}
std::unique_ptr<Base> data;
};
int main()
{
Copyable c(std::make_unique<Derived>());
Copyable c_copy = c;
std::cout << c_copy.data->f() << '\n';
}
Obviously the clone code works. Thing is, there's some things in it I'd like to avoid:
raw new.
a random function that needs to be part of the interface.
This function returns a raw pointer.
Every user of this class that wants to be copyable needs to call this function.
So, is there a "clean" alternative?
Note I want to use smart pointers for all the obvious reasons, I just need a deep copying std::unique_ptr. Something like std::copyable_unique_ptr, combining optional move semantics with a deep copying copy constructor. Is this the cleanest way? Or does that only add the the confusion?
You can certainly create a clone_ptr-class for any object you know statically how to clone.
It would hold a pointer to the object, and a pointer to a function for cloning said object, probably from converting a stateless lambda.
Suppose I have class A and two derived classes, B and C, e.g.:
#include <iostream>
#include <list>
#include <string>
using namespace std;
class A {
public:
virtual void poke() const = 0;
virtual ~A() {};
};
class B : public A {
string _response;
public:
B(const string& response) : _response(response) {}
void poke () const {
cout << _response << endl;
}
};
class C : public A {
string _response;
public:
C(const string& response) : _response(response) {}
void poke () const {
cout << "Well, " << _response << endl;
}
};
Can I somehow initialize an std::list using the following initializer list: {B("Me"), C("and you")}, so that polymorphism works when I iterate over the list and call poke() (i.e., no slicing occurs)? I guess I need to define an std::list<Smth>, where Smth accepts temporary objects, has a copy constructor that does move semantics inside (because initialization lists seem to be doing copying and not moving), and supports smart pointers so I can iterate with it and do (*it)->poke(). Just for clarity, I want to be be able to write:
list<Smth> test {B("Me"), C("and you")};
for(auto it = test.begin(); it != test.end(); it++) {
(*it)->poke();
}
I was trying to find a simple solution but I got to the point where my program compiled but generated run time errors, and so I gave up at that point... Maybe somehow make a unique pointer out of a temporary object? Or can I use && somehow?
For polymorphism, you need a reference or a pointer. Both will become dangling as soon as the sentence ends, because even if you bound those objects to them somehow, you bound them to temporary objects.
The usual solution is to dynamically allocate and create the objects and holding them with pointers. This means something like the following (I also changed the loop to C++11 style, instead of using iterators directly):
std::list<std::unique_ptr<A>> test {
std::make_unique<B>("Me"), std::make_unique<C>("and you")};
for(const auto& p : test) {
p->poke();
}
Well, if I am willing to do extra copying of B and C and use a shared_ptr instead of a unique_ptr, then the following example works (I am not saying it is good programming style, but it does show the cost of having a convenient notation with initialization lists):
#include <iostream>
#include <list>
#include <memory>
#include <string>
using namespace std;
class A {
public:
virtual void poke() const = 0;
virtual ~A() {}
};
class B : public A {
string _response;
public:
B(const string& response) : _response(response) {}
void poke () const {
cout << _response << endl;
}
operator shared_ptr<A>() {
return make_shared<B>(*this);
}
};
class C : public A {
string _response;
public:
C(const string& response) : _response(response) {}
void poke () const {
cout << "Well, " << _response << endl;
}
operator shared_ptr<A>() {
return make_shared<C>(*this);
}
};
int main() {
list<shared_ptr<A>> test {B("Me"), C("and you")};
for(const auto& it : test) {
it->poke();
}
}
Reading Scott Meyer's book "Effective Modern C++", Item 24 (and following), and Item 41, I wonder that this book opposes:
the individual constructors for lvalue and rvalue parameters
to
a template'd universal constructor solution
It says, that 1. has the disadvantage to duplicate code.
Whereas 2. has the disadvantage to potentially being used for unwanted types.
I wonder why the book does not mention a mixed model - as in the example code shown below.
It uses type-safe dedicated constructors for lvalue and rvalue but delegates to a single (private) generic implementation for "universal reference".
This avoids unwanted template types of a public "universal reference" constructor.
So is there is anything wrong with the approach below? Something I missed?
#include <iostream>
#include <string>
class MyClass
{
private:
enum class Dummy { Nop = 0 } ;
template <class T>
MyClass(Dummy, T&& data)
: _data(std::forward<T>(data))
{
std::cout << "MyClass universal reference template c'tor" << std::endl;
}
public:
// proxy c'tors delegating to universal reference c'tor
MyClass (std::string const & data)
: MyClass(Dummy::Nop, data)
{
std::cout << "MyClass lvalue c'tor" << std::endl;
}
MyClass (std::string && data)
: MyClass(Dummy::Nop, std::move(data))
{
std::cout << "MyClass rvalue c'tor" << std::endl;
}
private:
std::string _data;
};
int main(int, char**)
{
{
std::string str("demo");
MyClass myClass(str);
}
{
MyClass myClass("hello, world");
}
return 0;
}
And now let's put the book down and do it the right way:
Pros:
Optimal efficiency
Correct type limitations
DRY
Cons:
None
-
#include <iostream>
#include <string>
#include <type_traits>
class MyClass
{
public:
template <class T, std::enable_if_t<std::is_constructible<std::string, T>::value>* = nullptr>
MyClass(T&& data)
: _data(std::forward<T>(data))
{
std::cout << "MyClass universal reference template c'tor" << std::endl;
}
private:
std::string _data;
};
int main()
{
using namespace std::string_literals;
auto a = MyClass("hello"s);
auto b = MyClass("world");
const auto s = "Hello, World"s;
auto s2 = "Hello, World";
auto c = MyClass(s);
auto d = MyClass(s2);
// won't compile
// auto e = MyClass(10);
}
I'm not sure of the title, because I'm not sure the issue comes from the "copyablility" of my container.
I tryied quite everything but I can't get rid of this error.
Here is a simplified version of my code (please do not challenge the class design, I really would like to keep the end-used syntax in the BOOST_FOREACH):
template <typename T>
class MyContainer
{
public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
MyContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_lock(mutex)
{
}
iterator begin() { return m_vector.begin(); }
const_iterator begin() const { return m_vector.begin(); }
iterator end() { return m_vector.end(); }
const_iterator end() const { return m_vector.end(); }
private:
std::vector<T>& m_vector;
boost::lock_guard<boost::mutex> m_lock;
};
template <typename T>
struct GetContainer
{
GetContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_mutex(mutex)
{
}
MyContainer<T> Get()
{
return MyContainer<T>(m_vector, m_mutex);
}
std::vector<T>& m_vector;
boost::mutex& m_mutex;
};
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
boost::mutex m;
GetContainer<int> getter(v, m);
BOOST_FOREACH(int i, getter.Get())
{
std::cout << i << std::endl;
}
return 0;
}
The compiler complains about not having a copy constructor for MyContainer::MyContainer(const MyContainer&).
I also have :
error: no matching function for call to ‘MyContainer::MyContainer(boost::foreach_detail_::rvalue_probe >::value_type)’
I follow the extensibility tips:
http://www.boost.org/doc/libs/1_58_0/doc/html/foreach/extensibility.html#foreach.extensibility.making__literal_boost_foreach__literal__work_with_non_copyable_sequence_types
But, making
MyContainer<T> : private boost::noncopyable
doesn't solve the issue.
Nor defining the function
boost_foreach_is_noncopyable
or specializing the template struct
is_noncopyable
for MyContainer (in fact, how would I specialize this template for a template type ?)
Last "tip":
If I remove the mutex and the lock from everywhere (I just pass the vector to GetContainer and to MyContainer), it works.
But it doesn't work if I make
MyContainer<T> : private boost::noncopyable
(I expected it should, so I'm not sure my problem is with BOOST_FOREACH, but maybe because I return a copy of MyContainer with my getter ?)
I thank you if you read me until here, and thanks in advance for help.
Seems to be a limitation of BOOST_FOREACH with move-only types. I didn't find a way around it¹ (except for the - ugly - obvious approach to put the lock_guard in a shared_ptr).
You didn't specify a c++03 requirement, though, so you can make it work without BOOST_FOREACH by replacing lock_guard with unique_lock.
Here's my take on things in c++11 (note how generic it is):
Live On Coliru
#include <boost/thread.hpp>
#include <boost/range.hpp>
namespace detail {
template <typename R, typename M>
struct RangeLock {
RangeLock(R&r, M& m) : _r(r), _l(m) {}
RangeLock(RangeLock&&) = default;
using iterator = typename boost::range_iterator<R>::type;
iterator begin() { using std::begin; return begin(_r); }
iterator end () { using std::end; return end (_r); }
using const_iterator = typename boost::range_iterator<R const>::type;
const_iterator begin() const { using std::begin; return begin(_r); }
const_iterator end () const { using std::end; return end (_r); }
private:
R& _r;
boost::unique_lock<M> _l;
};
}
template <typename R, typename M>
detail::RangeLock<R,M> make_range_lock(R& r, M& mx) { return {r,mx}; }
template <typename R, typename M>
detail::RangeLock<R const,M> make_range_lock(R const& r, M& mx) { return {r,mx}; }
#include <vector>
#include <map>
int main() {
boost::mutex mx;
std::vector<int> const vec { 1, 2 };
std::map<int, std::string> const map { { 1, "one" }, { 2, "two" } };
for(int i : make_range_lock(vec, mx))
std::cout << i << std::endl;
for(auto& p : make_range_lock(map, mx))
std::cout << p.second << std::endl;
for(auto& p : make_range_lock(boost::make_iterator_range(map.equal_range(1)), mx))
std::cout << p.second << std::endl;
}
Prints
1
2
one
two
one
¹ not even using all the approaches from Using BOOST_FOREACH with a constant intrusive list
I post my answer if it can help...
With C++03, I finally provide a copy constructor to be able to use the class with BOOST_FOREACH.
So the issue is moved to another topic: make the class copied in a logic and suitable way.
In my case, I "share the lock and the vector", the user shouldn't use this copy itself if he doesn't want to do bugs, but in BOOST_FOREACH it's okay:
I change the mutex to a recursive_mutex
I change the lock to an unique_lock
and:
MyContainer(const MyContainer& other) :
m_vector(other.vec),
m_lock(*other.m_lock.mutex())
{
}
With C++11
Thanks to Chris Glover on the boost mailling list, a C++11 solution:
You can't do what you are trying to do in C++03. To accomplish it, you
need C++11 move semantics to be able to move the MyContainer out of the Get
function. Even without using BOOST_FOREACH, the following code fails;
GetContainer<int> getter(v, m);
MyContainer<int> c = getter.Get(); // <-- Error.
Here's an example with the necessary changes; I changed the scoped_lock to
a unique_lock and added a move constructor.
template <typename T>
class MyContainer
{
public:
[...]
MyContainer(MyContainer&& other)
: m_vector(other.m_vector)
{
m_lock = std::move(other.m_lock);
other.m_vector = nullptr;
}