I try to send to function a shared_ptr with polymorphic class.
My objective is to find a best way to send my shared_ptr
without increase ref_count.
EDIT: I don't search solution where my shared_ptr is replaced because I want to call shared_ptr.reset() for example.
Currently, void doGenericTemplate(std::shared_ptr<CLASS>& ptr) is what I want in result BUT I prefer a single function in program.
Do you have another solution ?
Moreover, I don't understand why the function void doGeneric(std::shared_ptr<Base>& ptr) doesn't compile (equivalent without shared_ptr work fine: please check doClassic in complete code).
Do you have an explain ?
Thanks you !
Partial code
#include <iostream>
#include <memory>
class Base
{
public:
Base() = default;
virtual ~Base() = default;
virtual void run() = 0;
};
class Derived1: public Base
{
public:
Derived1() = default;
virtual ~Derived1() = default;
void run()
{
std::cout << " Derived1";
}
};
class Derived2: public Base
{
public:
Derived2() = default;
virtual ~Derived2() = default;
void run()
{
std::cout << " Derived2";
}
};
// This function works but increase count
void doGenericCopy(std::shared_ptr<Base> ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// This function works without increase count = OK !
void doSpecificD1(std::shared_ptr<Derived1>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// Compilation error = FAILED !
void doGeneric(std::shared_ptr<Base>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// Working fine for all Derivate = OK !
template<typename CLASS>
void doGenericTemplate(std::shared_ptr<CLASS>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
int main()
{
auto d1 = std::make_shared<Derived1>();
auto d2 = std::make_shared<Derived2>();
std::cout << "With copy: " << std::endl;
doGenericCopy(d1);
doGenericCopy(d2);
std::cout << "Specific: " << std::endl;
doSpecificD1(d1);
std::cout << "Template: " << std::endl;
doGenericTemplate(d1);
doGenericTemplate(d2);
// Compilation issue
//doGeneric(d1);
}
Complete code
https://ideone.com/ZL0v7z
Conclusion
Currently in c++, shared_ptr has not in language a specific tools to use polymorphism of class inside template.
The best way is to refactor my code and avoids to manage shared_ptr (ref_count, reset).
Thanks guys !
Do you have another solution ?
Pass object by reference or const reference instead of shared_ptr.
void doGeneric(Base& r)
{
r.run();
}
Firstly - this shows explicitly that you do not store or cache pointer somwhere. Secondly - you avoid ambiguities like the one you presented here.
Do you have an explain ?
Passing shared_ptr<Derived> to function causes implicit cast to shared_ptr<Base>. This new shared_ptr<Base> is temporary, so it can not be cast to shared_ptr<Base> &. This implicit cast would increase ref count even if you could pass it.
A shared_ptr<Base> and shared_ptr<Derived> are unrelated types, except you can implicitly create a shared_ptr<Base> from a shared_ptr<Derived>.
This creation adds a reference count.
If you really, really want to avoid that reference count...
template<class T>
struct shared_ptr_view {
template<class D>
shared_ptr_view( std::shared_ptr<D>& sptr ):
vtable( get_vtable<D>() ),
ptr( std::addressof(sptr) )
{}
shared_ptr_view( shared_ptr_view const& ) = default;
shared_ptr_view() = default;
shared_ptr_view& operator=( shared_ptr_view const& ) = delete;
T* get() const { if(vtable) return vtable->get(ptr); return nullptr; }
void clear() const { if(vtable) vtable->clear(ptr); }
std::shared_ptr<T> copy() const { if(vtable) return vtable->copy(ptr); return {} }
operator std::shared_ptr<T>() const { return copy(); }
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
explicit operator bool() const { return get(); }
std::size_t use_count() const { if (vtable) return vtable->use_count(ptr); return 0; }
private:
struct vtable_t {
T*(*get)(void*) = 0;
std::shared_ptr<T>(*copy)(void*) = 0;
void(*clear)(void*) = 0;
std::size_t(*use_count)(void*) = 0;
};
vtable_t const* vtable = 0;
void* ptr = 0;
template<class D>
static vtable_t create_vtable() {
return {
[](void* ptr)->T*{ return static_cast<std::shared_ptr<D>*>(ptr)->get(); },
[](void* ptr)->std::shared_ptr<T>{ return *static_cast<std::shared_ptr<D>*>(ptr); },
[](void* ptr){ static_cast<std::shared_ptr<D>*>(ptr)->reset(); },
[](void* ptr){ return static_cast<std::shared_ptr<D>*>(ptr)->use_count(); }
};
}
template<class D>
static vtable_t const* get_vtable() {
static const auto vtable = create_vtable<D>();
return &vtable;
}
};
then
void doGeneric( shared_ptr_view<Base> ptr ) {
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
does not increase the reference count. I think it is raw insanity.
shared_ptr_view.clear() works, but shared_ptr_view.reset(T*) cannot: a shared_ptr_view<Derived> cannot be reset to point to a Base*.
Related
I want to store some data to container. For example I have such code:
#include <iostream>
#include <string>
#include <memory>
#include <map>
class Base
{
public:
Base() {}
virtual ~Base() {}
};
class Class1 : public Base
{
public:
Class1() : Base() {}
~Class1() {}
};
class Class2 : public Base
{
public:
Class2() : Base() {}
~Class2() {}
};
class Class3 : public Base
{
public:
Class3() : Base() {}
~Class3() {}
};
std::map<std::string, std::shared_ptr<Base>> myContainer;
void save(const std::string& id, std::shared_ptr<Base> obj)
{
auto obj1 = std::dynamic_pointer_cast<Class1>(obj);
if (obj1)
{
std::cout << "save obj1" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj1))
);
}
auto obj2 = std::dynamic_pointer_cast<Class2>(obj);
if (obj2)
{
std::cout << "save obj2" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj2))
);
}
auto obj3 = std::dynamic_pointer_cast<Class3>(obj);
if (obj3)
{
std::cout << "save obj3" << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj3))
);
}
}
int main()
{
std::shared_ptr<Class1> a1 = std::make_shared<Class1>();
std::shared_ptr<Class2> a2 = std::make_shared<Class2>();
std::shared_ptr<Class3> a3 = std::make_shared<Class3>();
save("id1", a1);
save("id2", a2);
save("id3", a3);
std::cout << "size is " << myContainer.size() << std::endl;
return 0;
}
But function save() has too much complicated implementation. How to make it easier? Somehow to get correct object type and invoke save() once but not in every checking. Maybe it possible to implement it with std::variant or std::tuple? What is much optimized solution you can propose?
You seem to understand virtual functions.
Your entire save function could be implemented as:
void save(const std::string& id, std::shared_ptr<Base> obj)
{
std::cout << "save " << obj->name() << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj))
);
}
name() would be a virtual function that returns the correct string for the type.
Note that this implementation always saves the pointer passed to it, while your implementation may not save anything.
Assuming you've provide a shared pointer containing the real class instead of a std::shared_ptr<Base> when calling the function, you can rewrite this as a template:
template<class T>
char const* TypeName();
template<>
char const* TypeName<Class1>() { return "obj1"; }
template<>
char const* TypeName<Class2>() { return "obj2"; }
template<>
char const* TypeName<Class3>() { return "obj3"; }
template<class T>
void save(const std::string& id, std::shared_ptr<T> obj)
{
std::cout << "save " << TypeName<T>() << std::endl;
myContainer.emplace(std::piecewise_construct,
std::make_tuple(id),
std::make_tuple(std::move(obj))
);
}
Let's say I have class Action
template<class T>
class Action {
public:
virtual ~Action() = default;
virtual void execute(T &object) const = 0;
};
which can be executed on some object of type T
Next, I have class Object
class Object {
public:
Object() : actions() {}
virtual ~Object() = default;
virtual const std::string getName() const = 0;
void addAction(const Action<Object> *action) {
actions.push_back(action);
}
void execute() {
for (auto &action : actions) {
action->execute(*this);
}
}
private:
std::vector<const Action<Object> *> actions;
};
which holds a vector of actions which can be executed at once.
Now, I have some concrete ObjectA
class ObjectA : public Object {
public:
const std::string getName() const override {
return "ObjectA";
}
};
and two concrete actions ActionA, ActionB
class ActionA : public Action<ObjectA> {
void execute(ObjectA &object) const override {
std::cout << "ActionA on " << object.getName() << std::endl;
}
};
class ActionB : public Action<ObjectA> {
void execute(ObjectA &object) const override {
std::cout << "ActionB on " << object.getName() << std::endl;
}
};
The usage is that I create an ObjectA, add both action to it and execute them.
int main() {
ObjectA object = ObjectA{};
object.addAction(reinterpret_cast<const Action<Object> *>(new ActionA()));
object.addAction(reinterpret_cast<const Action<Object> *>(new ActionB()));
// This is what I want to achieve instead of using reinterpret_cast
//object.addAction(new ActionA());
//object.addAction(new ActionB());
object.execute();
}
The output should be
ActionA on ObjectA
ActionB on ObjectA
The problem is that in order to compile it, I must use reinterpret_cast. The problem is probably the definition of std::vector<const Action<Object> *> actions; I would like to template this, so in ObjectA it is like std::vector<const Action<ObjectA> *> actions;
Is something like that possible?
Action<Object> and Action<ObjectA> are unrelated types in the C++ type system.
What more, Action<Object> permits itself to be called with more types than Action<ObjectA>. So ignoring the type system, an Action<ObjectA> cannot implement the contract that Action<Object> promises it can fulfill.
However, an Action<Object> can fulfill the promise that an Action<ObjectA> makes.
There are famous two kinds of OO-type relations; covariance and contravariance. Action<T> is cotravariant in T, Action<Base> can be used to fulfill the contract of Action<Derived>.
So an approach.
First, your Action<T> is a poorly written pointer-semantic version of std::function<void(T&)>. Use that instead.
You now have value semantics.
template<class T>
using Action=std::function<void(T&)>;
class Object {
public:
Object() = default;
virtual ~Object() = default;
virtual const std::string getName() const = 0;
void addAction(Action<Object> action) {
actions.emplace_back(std::move(action));
}
void execute() {
for (auto &action : actions) {
action(*this);
}
}
private:
std::vector<Action<Object>> actions;
};
ah, much nicer.
This doesn't, however, solve your problem.
auto ActionA = Action<ObjectA>{
[](ObjectA &object) {
std::cout << "ActionA on " << object.getName() << std::endl;
}
};
ActionA cannot be assigned to an Action<Object> because an Action<Object> can be passed a non-ObjectA and it must do something with it.
Your original code's override won't compile.
We have to decide if we want to pretend to be an Action<Object> what we should do if the types mismatch? Here is one option:
template<class T, class F>
auto only_when_dynamic_type_matches( F&& f ) {
if constexpr( std::is_pointer< T >{} ) {
return
[f=std::forward<F>(f)](auto* x)->void{
auto* t = dynamic_cast<T>(x);
if (!t) return
f(t);
};
} else {
return
[f=std::forward<F>(f)](auto&& x)->void{
auto* t = dynamic_cast<std::remove_reference_t<T>*>(std::addressof(x));
if (!t) return;
f(*t);
};
}
}
now we can write
auto ActionA = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
std::cout << "ActionA on " << object.getName() << std::endl;
});
auto ActionB = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
std::cout << "ActionB on " << object.getName() << std::endl;
});
then
int main() {
ObjectA object = ObjectA{};
object.addAction(ActionA);
object.addAction(ActionB);
object.execute();
}
Live example.
I'm debugging some code and I ran across this snippet that was crashing, the problem is that the singleton pointer and the local pointer are not the same address. I'm not sure why that is.
The code roughly looks like this:
class B;
class I_A : public std::enable_shared_from_this<I_A> {
public:
virtual std::shared_ptr<B> make() = 0;
};
class A : public I_A {
public:
std::shared_ptr<B> make() override {
return std::make_shared<B>(shared_from_this());
}
};
static std::shared_ptr<I_A> getInstance() {
static std::shared_ptr<I_A> ptr;
if(!ptr) {
std::cout << "Making new instance\n";
ptr = std::make_shared<A>();
}
std::cout << "Ptr is " << ptr << '\n';
return ptr;
}
class B {
public:
B(const std::shared_ptr<I_A>& ptr) : ptr(ptr) {
std::cout << "In B() " << ptr << '\n';
}
void foo() {
std::cout << "B::ptr " << ptr << '\n';
assert(ptr == getInstance());
}
private:
const std::shared_ptr<I_A>& ptr; // Potential problem here
};
int main() {
auto bPtr = getInstance()->make();
bPtr->foo();
}
But if I store a copy of shared_ptr<I_A> in B instead of storing a ref, everything works fine.
If my understanding is correct, the singleton should never change the address, but the B::ptr and getInstance() have different addresses.
Any idea why that is?
You should (approximately) never use const std::shared_ptr<T>&, for any T. Just use a std::shared_ptr<T>. Would you use a T * const &?
The problem you are observing is that the instance of std::shared_ptr<I_A> that you get from A::shared_from_this() is a temporary, and it has ceased to exist by the time you can use your B. You then use a dangling reference, which has undefined behaviour.
Using a std::shared_ptr expresses shared ownership and optionality (with its possibility to be null).
I find myself in situations where I want to express shared ownership only in my code, and no optionality. When using a shared_ptr as a function parameter I have to let the function check that it is not null to be consistent/safe.
Passing a reference instead of course is an option in many cases, but I sometimes would also like to transfer the ownership, as it is possible with a shared_ptr.
Is there a class to replace shared_ptr without the possibility to be null, some convention to handle this problem, or does my question not make much sense?
You are asking for not_null wrapper class. Fortunately your issue is already addressed by C++ experts guideline and there are already example implementations - like this one. Search for not_null class template.
You could write a wrapper around std::shared_ptr that only allows creation from non-null:
#include <memory>
#include <cassert>
template <typename T>
class shared_reference
{
std::shared_ptr<T> m_ptr;
shared_reference(T* value) :m_ptr(value) { assert(value != nullptr); }
public:
shared_reference(const shared_reference&) = default;
shared_reference(shared_reference&&) = default;
~shared_reference() = default;
T* operator->() { return m_ptr.get(); }
const T* operator->() const { return m_ptr.get(); }
T& operator*() { return *m_ptr.get(); }
const T& operator*() const { return *m_ptr.get(); }
template <typename XT, typename...XTypes>
friend shared_reference<XT> make_shared_reference(XTypes&&...args);
};
template <typename T, typename...Types>
shared_reference<T> make_shared_reference(Types&&...args)
{
return shared_reference<T>(new T(std::forward<Types>(args)...));
}
Please note that operator= is missing yet. You should definitely add it.
You can use it like this:
#include <iostream>
using std::cout;
using std::endl;
struct test
{
int m_x;
test(int x) :m_x(x) { cout << "test("<<m_x<<")" << endl; }
test(const test& t) :m_x(t.m_x) { cout << "test(const test& " << m_x << ")" << endl; }
test(test&& t) :m_x(std::move(t.m_x)) { cout << "test(test&& " << m_x << ")" << endl; }
test& operator=(int x) { m_x = x; cout << "test::operator=(" << m_x << ")" << endl; return *this;}
test& operator=(const test& t) { m_x = t.m_x; cout << "test::operator=(const test& " << m_x << ")" << endl; return *this;}
test& operator=(test&& t) { m_x = std::move(t.m_x); cout << "test::operator=(test&& " << m_x << ")" << endl; return *this;}
~test() { cout << "~test(" << m_x << ")" << endl; }
};
#include <string>
int main() {
{
auto ref = make_shared_reference<test>(1);
auto ref2 = ref;
*ref2 = test(5);
}
{
test o(2);
auto ref = make_shared_reference<test>(std::move(o));
}
//Invalid case
//{
// test& a = *(test*)nullptr;
// auto ref = make_shared_reference<test>(a);
//}
}
Output:
test(1)
test(5)
test::operator=(test&& 5)
~test(5)
~test(5)
test(2)
test(test&& 2)
~test(2)
~test(2)
Example on Coliru
I hope I didn't forget anything that might result in undefined behaviour.
After taking a look at GSL's not_null class, which calls std::terminate() instead of abort();
Here is how I achieved it:
template <typename T>
class NonNull : public std::shared_ptr<T> {
typedef std::shared_ptr<T> super;
public:
inline NonNull()
: super(new T())
{
if ( ! super::get()) {
abort(); // Out of memory.
}
}
inline explicit NonNull(T *ptr)
: super(ptr)
{
if ( ! super::get()) {
abort(); // Input was null.
}
}
}
Basically, forces us to construct the class of T type.
Usage:
// Directly is a `std::shared_ptr` type:
NonNull<MyClass> myVariable;
// Unlike:
gsl::not_null<std::shared_ptr<MyClass > > myVariable;
I'm trying to provide a uniform interface for two similar types, one dealing with doubles and the other with floats.
class float_type {
float_type() { /* does floaty stuff */ }
float f();
};
class double_type {
double_type() { /* does doubly stuff */ }
double f();
};
I want to write a class that allocates one or the other depending on what the program needs to do.
I'm perfectly fine with the result of float_type::f() being converted to double. In fact, it happens anyway.
I tried to write it like this:
class union_type {
bool is_double;
char mem[ sizeof(double_type) > sizeof(float_type)
? sizeof(double_type) : sizeof(float_type) ];
public:
float_or_double_value_reader(bool is_double)
: is_double(is_double)
{
if (is_double) new(mem) double_type();
else new(mem) float_type();
}
~float_or_double_value_reader() {
if (is_double) delete static_cast<double_type*>(mem);
else delete static_cast< float_type*>(mem);
}
double f() {
return (is_doubled
? static_cast<double_type*>(mem)->f()
: static_cast< float_type*>(mem)->f()
);
}
};
But I get invalid static_cast from type 'char [128]' to type 'double_type'.
I know I could add a member pointers to point to what new returns,
but that would be redundant, since I already know where mem is located,
so I want to avoid that.
If I use reinterpret_cast instead, I get free(): invalid pointer: at runtime when the union_type is destroyed.
What's the appropriate method of casting here?
reinterpret_cast should be the appropriate method of casting.
However, you can't simply delete reinterpret_cast<double_type*>(mem) because that will not only destroy the object, but also free the memory as if it was allocated with new - which it wasn't.
You can use reinterpret_cast<double_type*>(mem)->~double_type(); to destroy the object without attempting to free the memory.
Of course the above applies to float_type as well.
A better option would be to provide casting operator.
I would have provided a implicit double casting operator to the float class to achieve the same interface
You could use a template base class:
#include <iostream>
template < typename T >
class base_decimal
{
public:
base_decimal(T data) : _data(data) {}
virtual ~base_decimal() {}
T f() { return this->_data; }
base_decimal& operator=(T val) { this->_data = val; }
operator T() { return this->_data; }
friend std::ostream& operator<<(std::ostream& os, const base_decimal& bd)
{
os << bd._data;
return os;
}
private:
T _data;
};
class float_type : public base_decimal<float>
{
public:
float_type(float f) : base_decimal<float>(f)
{
// do float stuff
}
};
class double_type : public base_decimal<double>
{
public:
double_type(double d) : base_decimal<double>(d)
{
// do double stuff
}
};
int main(int argc, char* argv[])
{
float_type f = 1.2f;
double_type d = 2.2;
std::cout << "f = " << f << std::endl;
std::cout << "d = " << d << std::endl;
double rd = d;
double rf = f;
std::cout << "rf = " << rf << std::endl;
std::cout << "rd = " << rd << std::endl;
return 0;
}