I am trying to create a QList of a polymorphic type that still uses Qt's implicit sharing.
My specific use case is passing items held in a QList to QtConcurrent::mapped. The items all descend from a base class which defines a virtual function that QtConcurrent::mapped will call. The majority of the stored data will be child class specific. These items may be edited after the threading begins, leaving me with two main options, locks or copy the data. I do not want to stick locks in, because that would eliminate most of the purpose of using extra threads. Also making full copies of my data also seems quite undesirable. Instead I would like use Qt's implicit sharing to only make copies of the data items that I change, however I can't seem to make a QList of a polymorphic type that still uses implicit sharing.
QList by default uses implicit sharing, so at first glance it would seem that we are already done.
QList<Base> list;
Derived derived_obj;
list.append(derived_obj); // this fails
However appending a child class to a QList of the parent class will not work and the standard answer is to instead use a QList of QSharedPointers to the base class, which will accept appending a pointer to the child class.
QList<QSharedPointer<Base> > pointer_list;
QSharedPointer<Derived> derived_pointer;
pointer_list.append(derived_pointer); // this works but there is no copy-on-write
If I use a QList of QSharedPointers, it is the QSharedPointer that will be be copied rather than my polymorphic class, meaning that I have lost the copy-on-write functionality that I would like.
I have also looked at using a QList of QSharedDataPointers.
QList<QSharedDataPointer<Base> > data_pointer_list;
QSharedDataPointer<Derived> derived_data_pointer;
list.append(derived_data_pointer); // this fails
However like QList itself, QSharedDataPointers do not seem to accept polymorphic types.
This fails:
QList<QSharedDataPointer<Base>> list;
QSharedDataPointer<Derived> derived(new Derived);
list.append(derived);
Note An alternative approach to the below would be to merge the PolymorphicShared and PolymorphicSharedBase to add polymorphism support directly to QSharedDataPointer, without placing special requirements on the QSharedData-derived type (e.g. it wouldn't need to explicitly support clone). This requires a bit more work. The below is just one working approach.
QSharedDataPointer is indeed the answer you seek and can definitely hold polymorphic QSharedData. You need to separate the type into a hierarchy based on QSharedData, and another parallel hierarchy wrapping the QSharedDataPointer. The QSharedDataPointer is not usually meant to be used directly by the end user of a class. It's an implementation detail useful in implementing an implicitly shared class.
For efficiency's sake, a QSharedDataPointer is a small type that can be moved at the bit level. It's quite efficient when stored in containers of all sorts - especially in Qt containers that can utilize the type traits to be aware of this property. The size of a class using a QSharedDataPointer will usually double if we make it polymorphic itself, thus it helps not to do it. The pointed-to data type can be polymorphic, of course.
First, let's define a rather univeral base class PIMPL that you'll build the hierarchy on. The PIMPL class can be dumped into the debug stream, and cloned.
// https://github.com/KubaO/stackoverflown/tree/master/questions/implicit-list-44593216
#include <QtCore>
#include <type_traits>
class PolymorphicSharedData : public QSharedData {
public:
virtual PolymorphicSharedData * clone() const = 0;
virtual QDebug dump(QDebug) const = 0;
virtual ~PolymorphicSharedData() {}
};
The xxxData types are PIMPLs and are not meant for use by the end-user. The user is meant to use the xxx type itself. This shared type then wraps the polymorphic PIMPL and uses the QSharedDataPointer for storage of the PIMPL. It exposes the methods of the PIMPL.
The type itself is not polymorphic, to save on the size of the virtual table pointer. The as() function acts as dynamic_cast() would, by redirecting polymorphism to the PIMPL.
class PolymorphicShared {
protected:
QSharedDataPointer<PolymorphicSharedData> d_ptr;
PolymorphicShared(PolymorphicSharedData * d) : d_ptr(d) {}
public:
PolymorphicShared() = default;
PolymorphicShared(const PolymorphicShared & o) = default;
PolymorphicShared & operator=(const PolymorphicShared &) = default;
QDebug dump(QDebug dbg) const { return d_ptr->dump(dbg); }
template <class T> typename
std::enable_if<std::is_pointer<T>::value, typename
std::enable_if<!std::is_const<typename std::remove_pointer<T>::type>::value, T>::type>
::type as() {
if (dynamic_cast<typename std::remove_pointer<T>::type::PIMPL*>(d_ptr.data()))
return static_cast<T>(this);
return {};
}
template <class T> typename
std::enable_if<std::is_pointer<T>::value, typename
std::enable_if<std::is_const<typename std::remove_pointer<T>::type>::value, T>::type>
::type as() const {
if (dynamic_cast<const typename std::remove_pointer<T>::type::PIMPL*>(d_ptr.data()))
return static_cast<T>(this);
return {};
}
template <class T> typename
std::enable_if<std::is_reference<T>::value, typename
std::enable_if<!std::is_const<typename std::remove_reference<T>::type>::value, T>::type>
::type as() {
Q_UNUSED(dynamic_cast<typename std::remove_reference<T>::type::PIMPL&>(*d_ptr));
return static_cast<T>(*this);
}
template <class T> typename
std::enable_if<std::is_reference<T>::value, typename
std::enable_if<std::is_const<typename std::remove_reference<T>::type>::value, T>::type>
::type as() const {
Q_UNUSED(dynamic_cast<const typename std::remove_reference<T>::type::PIMPL&>(*d_ptr));
return static_cast<T>(*this);
}
int ref() const { return d_ptr ? d_ptr->ref.load() : 0; }
};
QDebug operator<<(QDebug dbg, const PolymorphicShared & val) {
return val.dump(dbg);
}
Q_DECLARE_TYPEINFO(PolymorphicShared, Q_MOVABLE_TYPE);
#define DECLARE_TYPEINFO(concreteType) Q_DECLARE_TYPEINFO(concreteType, Q_MOVABLE_TYPE)
template <> PolymorphicSharedData * QSharedDataPointer<PolymorphicSharedData>::clone() {
return d->clone();
}
A helper to makes it easy to use the abstract base class with derived data types. It casts the d-ptr to a proper derived PIMPL type, and forwards the constructor arguments to the PIMPL's constructor.
template <class Data, class Base = PolymorphicShared> class PolymorphicSharedBase : public Base {
friend class PolymorphicShared;
protected:
using PIMPL = typename std::enable_if<std::is_base_of<PolymorphicSharedData, Data>::value, Data>::type;
PIMPL * d() { return static_cast<PIMPL*>(&*this->d_ptr); }
const PIMPL * d() const { return static_cast<const PIMPL*>(&*this->d_ptr); }
PolymorphicSharedBase(PolymorphicSharedData * d) : Base(d) {}
template <typename T> static typename std::enable_if<std::is_constructible<T>::value, T*>::type
construct() { return new T(); }
template <typename T> static typename std::enable_if<!std::is_constructible<T>::value, T*>::type
construct() { return nullptr; }
public:
using Base::Base;
template<typename ...Args,
typename = typename std::enable_if<std::is_constructible<Data, Args...>::value>::type
> PolymorphicSharedBase(Args&&... args) :
Base(static_cast<PolymorphicSharedData*>(new Data(std::forward<Args>(args)...))) {}
PolymorphicSharedBase() : Base(construct<Data>()) {}
};
It's now a simple matter to have the parallel hierarchy of PIMPL types and their carriers. First, a basic abstract type in our hierarchy that adds two methods. Note how PolymorphicSharedBase adds the d() accessor of the correct type.
class MyAbstractTypeData : public PolymorphicSharedData {
public:
virtual void gurgle() = 0;
virtual int gargle() const = 0;
};
class MyAbstractType : public PolymorphicSharedBase<MyAbstractTypeData> {
public:
using PolymorphicSharedBase::PolymorphicSharedBase;
void gurgle() { d()->gurgle(); }
int gargle() const { return d()->gargle(); }
};
DECLARE_TYPEINFO(MyAbstractType);
Then, a concrete type that adds no new methods:
class FooTypeData : public MyAbstractTypeData {
protected:
int m_foo = 0;
public:
FooTypeData() = default;
FooTypeData(int data) : m_foo(data) {}
void gurgle() override { m_foo++; }
int gargle() const override { return m_foo; }
MyAbstractTypeData * clone() const override { return new FooTypeData(*this); }
QDebug dump(QDebug dbg) const override {
return dbg << "FooType-" << ref << ":" << m_foo;
}
};
using FooType = PolymorphicSharedBase<FooTypeData, MyAbstractType>;
DECLARE_TYPEINFO(FooType);
And another type that adds methods.
class BarTypeData : public FooTypeData {
protected:
int m_bar = 0;
public:
BarTypeData() = default;
BarTypeData(int data) : m_bar(data) {}
MyAbstractTypeData * clone() const override { return new BarTypeData(*this); }
QDebug dump(QDebug dbg) const override {
return dbg << "BarType-" << ref << ":" << m_foo << "," << m_bar;
}
virtual void murgle() { m_bar++; }
};
class BarType : public PolymorphicSharedBase<BarTypeData, FooType> {
public:
using PolymorphicSharedBase::PolymorphicSharedBase;
void murgle() { d()->murgle(); }
};
DECLARE_TYPEINFO(BarType);
We'll want to verify that the as() method throws as needed:
template <typename F> bool is_bad_cast(F && fun) {
try { fun(); } catch (std::bad_cast) { return true; }
return false;
}
The use of the implicitly shared types is no different than the use of Qt's own such types. We can also cast using as instead of dynamic_cast.
int main() {
Q_ASSERT(sizeof(FooType) == sizeof(void*));
MyAbstractType a;
Q_ASSERT(!a.as<FooType*>());
FooType foo;
Q_ASSERT(foo.as<FooType*>());
a = foo;
Q_ASSERT(a.ref() == 2);
Q_ASSERT(a.as<const FooType*>());
Q_ASSERT(a.ref() == 2);
Q_ASSERT(a.as<FooType*>());
Q_ASSERT(a.ref() == 1);
MyAbstractType a2(foo);
Q_ASSERT(a2.ref() == 2);
QList<MyAbstractType> list1{FooType(3), BarType(8)};
auto list2 = list1;
qDebug() << "After copy: " << list1 << list2;
list2.detach();
qDebug() << "After detach: " << list1 << list2;
list1[0].gurgle();
qDebug() << "After list1[0] mod: " << list1 << list2;
Q_ASSERT(list2[1].as<BarType*>());
list2[1].as<BarType&>().murgle();
qDebug() << "After list2[1] mod: " << list1 << list2;
Q_ASSERT(!list2[0].as<BarType*>());
Q_ASSERT(is_bad_cast([&]{ list2[0].as<BarType&>(); }));
auto const list3 = list1;
Q_ASSERT(!list3[0].as<const BarType*>());
Q_ASSERT(is_bad_cast([&]{ list3[0].as<const BarType&>(); }));
}
Output:
After copy: (FooType-1:3, BarType-1:0,8) (FooType-1:3, BarType-1:0,8)
After detach: (FooType-2:3, BarType-2:0,8) (FooType-2:3, BarType-2:0,8)
After list1[0] mod: (FooType-1:4, BarType-2:0,8) (FooType-1:3, BarType-2:0,8)
After list2[1] mod: (FooType-1:4, BarType-1:0,8) (FooType-1:3, BarType-1:0,9)
The list copy was shallow and the items themselves weren't copied: the reference counts are all 1. After the detach, all data items were copied but because they are implicitly shared, they only incremented their reference counts. Finally, after an item is was modified, it is automatically detached, and the reference counts drop back to 1.
Related
This question is based on the example code below, which is inspired by Sean Parent's talk.
The goal of the code below is to provide an object wrapper similar to boost::any. I wrote this code to educate myself of type erasure. So, there is no practical uses this code intends (considering there is already boost::any).
class ObjWrap {
public:
template <typename T>
ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}
template <typename T>
friend typename T * getObjPtr(ObjWrap O) {
return static_cast<T*>(O.Self->getObjPtr_());
}
private:
struct Concept {
virtual ~Concept() = 0;
virtual void* getObjPtr_() = 0;
};
template <typename T>
struct Obj : Concept {
Obj(T O) : Data(std::move(O)) {}
void* getObjPtr_() { return static_cast<void*>(&Data); }
T Data;
};
std::unique_ptr<Concept> Self;
};
Before I can really ask my question, let's examine the code in the following aspects:
Concept::getObjPtr_ returns void* because a) Concept cannot be a template otherwise unique_ptr<Concept> Self would not work; b) void* is the only way I know how to return Obj::Data in a type-agnostic way in C++. Please correct me if this is wrong...
T * getObjPtr(ObjWrap O) is a template that needs instantiation separately from the ObjWrap constructor.
The use of ObjWrap basically includes: a) make a new ObjWrap over an existing object; b) retrieve the underlying object given an ObjWrap. For example:
ObjWrap a(1);
ObjWrap b(std::string("b"));
int* p_a = getObjPtr<int>(a);
std::string* p_b = getObjPtr<std::string>(b);
This works but it is obvious that getObjPtr<int>(b) does not work as intended.
So, my question is:
Is there a way to fix the above code so that we can simply use int* p_a = getObjPtr(a) and std::string* p_b = getObjPtr(b) or better yet auto p_a = getObjPtr(a) and auto p_b = getObjPtr(b)? In other words, is there a way in C++ to instantiate two templates at the same time (if so, we can instantiate the ObjWrap constructor and T* getObjPtr(ObjWrap) at compile time of a ObjWrap object, e.g., ObjWrap a(1))?
Edit 1:
Making ObjWrap a templated class does not help since it defeats the purpose of type erasure.
template <typename T>
class ObjWrap {
/* ... */
};
ObjWrap<int> a(1); // this is no good for type erasure.
Edit 2:
I was reading the code and realize that it can be modified to reflect the idea a little better. So, please also look at the following code:
class ObjWrap {
public:
template <typename T>
ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}
template <typename T>
T * getObjPtr() {
return static_cast<T*>(Self->getObjPtr_());
}
private:
struct Concept {
virtual ~Concept() = 0;
virtual void* getObjPtr_() = 0;
};
template <typename T>
struct Obj : Concept {
Obj(T O) : Data(std::move(O)) {}
void* getObjPtr_() { return static_cast<void*>(&Data); }
T Data;
};
std::unique_ptr<Concept> Self;
};
int main() {
ObjWrap a(1);
ObjWrap b(std::string("b"));
int* p_a = a.getObjPtr<int>();
std::string* p_b = b.getObjPtr<std::string>();
std::cout << *p_a << " " << *p_b << "\n";
return 0;
}
The main difference between this version of the code versus the one above is that T * getObjPtr() is a member function that is encapsulated by the ObjWrap object.
Edit 3:
My question regarding type erasure is answered by accepted answer. However, the question on simultaneous type instantiation to multiple templates is yet to be answered. My guess is currently C++ does not allow it but it would be nice to hear from people with more experience on that.
There are a few things that may help.
First thing to say is that if Obj ever needs to expose the address of the object, it's not Sean Parent's 'inheritance is the root of all evil' type-erasing container.
The trick is to ensure that the interface of Obj offers all semantic actions and queries the wrapper will ever need.
In order to provide this, it's often a reasonable idea to cache the address of the object and its type_id in the concept.
Consider the following updated example, in which there is one public method - operator==. The rule is that two Objs are equal if they contain the same type of object and those objects compare equal.
Note that the address and type_id:
1) are implementation details and not exposed on the interface of Obj
2) are accessible without virtual calls, which short-circuits the not-equal case.
#include <memory>
#include <utility>
#include <typeinfo>
#include <utility>
#include <cassert>
#include <iostream>
class ObjWrap
{
public:
template <typename T>
ObjWrap(T O) : Self(new Model<T>(std::move(O))) {}
// objects are equal if they contain the same type of model
// and the models compare equal
bool operator==(ObjWrap const& other) const
{
// note the short-circuit when the types are not the same
// this means is_equal can guarantee that the address can be cast
// without a further check
return Self->info == other.Self->info
&& Self->is_equal(other.Self->addr);
}
bool operator!=(ObjWrap const& other) const
{
return !(*this == other);
}
friend std::ostream& operator<<(std::ostream& os, ObjWrap const& o)
{
return o.Self->emit(os);
}
private:
struct Concept
{
// cache the address and type here in the concept.
void* addr;
std::type_info const& info;
Concept(void* address, std::type_info const& info)
: addr(address)
, info(info)
{}
virtual ~Concept() = default;
// this is the concept's interface
virtual bool is_equal(void const* other_address) const = 0;
virtual std::ostream& emit(std::ostream& os) const = 0;
};
template <typename T>
struct Model : Concept
{
Model(T O)
: Concept(std::addressof(Data), typeid(T))
, Data(std::move(O)) {}
// no need to check the pointer before casting it.
// Obj takes care of that
/// #pre other_address is a valid pointer to a T
bool is_equal(void const* other_address) const override
{
return Data == *(static_cast<T const*>(other_address));
}
std::ostream& emit(std::ostream& os) const override
{
return os << Data;
}
T Data;
};
std::unique_ptr<Concept> Self;
};
int main()
{
auto x = ObjWrap(std::string("foo"));
auto y = ObjWrap(std::string("foo"));
auto z = ObjWrap(int(2));
assert(x == y);
assert(y != z);
std::cout << x << " " << y << " " << z << std::endl;
}
http://coliru.stacked-crooked.com/a/dcece2a824a42948
(etc. etc.) Please correct me if this is wrong...
Your premise is wrong at least in principle, if not also in practice. You're insisting on making getObjPtr() a virtual method, and using an abstract base class. But - you've not established this is necessary. Remember - using virtual methods is expensive! Why should I pay for virtuals just to get type erasure?
Is there a way to fix the above code so that we can simply use int* p_a = getObjPtr(a)
Take Sean Parent's talk title to heart (as opposed to the fact that he does use inheritance in the talk), drop the inheritance and the answer should be Yes. Edit: It's sufficient for the code that erases the type and the code that un-erases the type to know what the type is - as long as you don't need to act on the type-erased data in a type-specific way. In Sean Parent's talk, you need to be able to make non-trivial copies of it, to move it, to draw it etc. With std::any/boost::any you might need copying and moving, which may require virtuals - but that's the most general use case.
Even std::any limits what you can and can't do, as is discussed in this question:
why doesn't std::any_cast support implicit conversion?
For example I have some function pet_maker() that creates and returns a Cat or a Dog as a base Pet. I want to call this function many many times, and do something with the Pet returned.
Traditionally I would new the Cat or Dog in pet_maker() and return a pointer to it, however the new call is much slower than doing everything on the stack.
Is there a neat way anyone can think of to return as an abstraction without having to do the new every time the function is called, or is there some other way that I can quickly create and return abstractions?
Using new is pretty much inevitable if you want polymorphism. But the reason new works slowly is because it looks for free memory every time. What you could do is write your own operator new, which could, in theory, for example use pre-allocated memory chunks and be very fast.
This article covers many aspects of what you might need.
Each allocation is an overhead so you may get benefits by allocating whole arrays of objects rather than one object at a time.
You could use std::deque to achieve this:
class Pet { public: virtual ~Pet() {} virtual std::string talk() const = 0; };
class Cat: public Pet { std::string talk() const override { return "meow"; }};
class Dog: public Pet { std::string talk() const override { return "woof"; }};
class Pig: public Pet { std::string talk() const override { return "oink"; }};
class PetMaker
{
// std::deque never re-allocates when adding
// elements which is important when distributing
// pointers to the elements
std::deque<Cat> cats;
std::deque<Dog> dogs;
std::deque<Pig> pigs;
public:
Pet* make()
{
switch(std::rand() % 3)
{
case 0:
cats.emplace_back();
return &cats.back();
case 1:
dogs.emplace_back();
return &dogs.back();
}
pigs.emplace_back();
return &pigs.back();
}
};
int main()
{
std::srand(std::time(0));
PetMaker maker;
std::vector<Pet*> pets;
for(auto i = 0; i < 100; ++i)
pets.push_back(maker.make());
for(auto pet: pets)
std::cout << pet->talk() << '\n';
}
The reason to use a std::deque is that it never reallocates its elements when you add new ones so the pointers that you distribute always remain valid until the PetMaker itself is deleted.
An added benefit to this over allocating objects individually is that they don't need to be deleted or placed in a smart pointer, the std::deque manages their lifetime.
Is there a neat way anyone can think of to return as an abstraction without having to do the new every time the function is called, or is there some other way that I can quickly create and return abstractions?
TL;DR: The function need not allocate if there is already sufficient memory to work with.
A simple way would be to create a smart pointer that is slightly different from its siblings: it would contain a buffer in which it would store the object. We can even make it non-nullable!
Long version:
I'll present the rough draft in reverse order, from the motivation to the tricky details:
class Pet {
public:
virtual ~Pet() {}
virtual void say() = 0;
};
class Cat: public Pet {
public:
virtual void say() override { std::cout << "Miaou\n"; }
};
class Dog: public Pet {
public:
virtual void say() override { std::cout << "Woof\n"; }
};
template <>
struct polymorphic_value_memory<Pet> {
static size_t const capacity = sizeof(Dog);
static size_t const alignment = alignof(Dog);
};
typedef polymorphic_value<Pet> any_pet;
any_pet pet_factory(std::string const& name) {
if (name == "Cat") { return any_pet::build<Cat>(); }
if (name == "Dog") { return any_pet::build<Dog>(); }
throw std::runtime_error("Unknown pet name");
}
int main() {
any_pet pet = pet_factory("Cat");
pet->say();
pet = pet_factory("Dog");
pet->say();
pet = pet_factory("Cat");
pet->say();
}
The expected output:
Miaou
Woof
Miaou
which you can find here.
Note that it is required to specify the maximum size and alignment of the derived values that can be supported. No way around that.
Of course, we statically check whether the caller would attempt to build a value with an inappropriate type to avoid any unpleasantness.
The main disadvantage, of course, is that it must be at least as big (and aligned) as its largest variant, and all this must be predicted ahead of time. This is thus not a silver bullet, but performance-wise the absence of memory-allocation can rock.
How does it work? Using this high-level class (and the helper):
// To be specialized for each base class:
// - provide capacity member (size_t)
// - provide alignment member (size_t)
template <typename> struct polymorphic_value_memory;
template <typename T,
typename CA = CopyAssignableTag,
typename CC = CopyConstructibleTag,
typename MA = MoveAssignableTag,
typename MC = MoveConstructibleTag>
class polymorphic_value {
static size_t const capacity = polymorphic_value_memory<T>::capacity;
static size_t const alignment = polymorphic_value_memory<T>::alignment;
static bool const move_constructible = std::is_same<MC, MoveConstructibleTag>::value;
static bool const move_assignable = std::is_same<MA, MoveAssignableTag>::value;
static bool const copy_constructible = std::is_same<CC, CopyConstructibleTag>::value;
static bool const copy_assignable = std::is_same<CA, CopyAssignableTag>::value;
typedef typename std::aligned_storage<capacity, alignment>::type storage_type;
public:
template <typename U, typename... Args>
static polymorphic_value build(Args&&... args) {
static_assert(
sizeof(U) <= capacity,
"Cannot host such a large type."
);
static_assert(
alignof(U) <= alignment,
"Cannot host such a largely aligned type."
);
polymorphic_value result{NoneTag{}};
result.m_vtable = &build_vtable<T, U, MC, CC, MA, CA>();
new (result.get_ptr()) U(std::forward<Args>(args)...);
return result;
}
polymorphic_value(polymorphic_value&& other): m_vtable(other.m_vtable), m_storage() {
static_assert(
move_constructible,
"Cannot move construct this value."
);
(*m_vtable->move_construct)(&other.m_storage, &m_storage);
m_vtable = other.m_vtable;
}
polymorphic_value& operator=(polymorphic_value&& other) {
static_assert(
move_assignable || move_constructible,
"Cannot move assign this value."
);
if (move_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->move_assign)(&other.m_storage, &m_storage);
}
else
{
(*m_vtable->destroy)(&m_storage);
m_vtable = other.m_vtable;
(*m_vtable->move_construct)(&other.m_storage, &m_storage);
}
return *this;
}
polymorphic_value(polymorphic_value const& other): m_vtable(other.m_vtable), m_storage() {
static_assert(
copy_constructible,
"Cannot copy construct this value."
);
(*m_vtable->copy_construct)(&other.m_storage, &m_storage);
}
polymorphic_value& operator=(polymorphic_value const& other) {
static_assert(
copy_assignable || (copy_constructible && move_constructible),
"Cannot copy assign this value."
);
if (copy_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->copy_assign)(&other.m_storage, &m_storage);
return *this;
}
// Exception safety
storage_type tmp;
(*other.m_vtable->copy_construct)(&other.m_storage, &tmp);
if (move_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->move_assign)(&tmp, &m_storage);
}
else
{
(*m_vtable->destroy)(&m_storage);
m_vtable = other.m_vtable;
(*m_vtable->move_construct)(&tmp, &m_storage);
}
return *this;
}
~polymorphic_value() { (*m_vtable->destroy)(&m_storage); }
T& get() { return *this->get_ptr(); }
T const& get() const { return *this->get_ptr(); }
T* operator->() { return this->get_ptr(); }
T const* operator->() const { return this->get_ptr(); }
T& operator*() { return this->get(); }
T const& operator*() const { return this->get(); }
private:
polymorphic_value(NoneTag): m_vtable(0), m_storage() {}
T* get_ptr() { return reinterpret_cast<T*>(&m_storage); }
T const* get_ptr() const { return reinterpret_cast<T const*>(&m_storage); }
polymorphic_value_vtable const* m_vtable;
storage_type m_storage;
}; // class polymorphic_value
Essentially, this is just like any STL container. The bulk of the complexity is in redefining the construction, move, copy and destruction. It's otherwise quite simple.
There are two points of note:
I use a tag-based approach to handling capabilities:
for example, a copy constructor is only available if the CopyConstructibleTag is passed
if the CopyConstructibleTag is passed, all types passed to build must be copy constructible
Some operations are provided even if the objects do not have the capability, as long as some alternative way of providing them exist
Obviously, all methods preserve the invariant that the polymorphic_value is never empty.
There is also a tricky detail related to assignments: assignment is only well-defined if both objects are of the same dynamic type, which we check with the m_vtable == other.m_vtable checks.
For completeness, the missing pieces used to power up this class:
//
// VTable, with nullable methods for run-time detection of capabilities
//
struct NoneTag {};
struct MoveConstructibleTag {};
struct CopyConstructibleTag {};
struct MoveAssignableTag {};
struct CopyAssignableTag {};
struct polymorphic_value_vtable {
typedef void (*move_construct_type)(void* src, void* dst);
typedef void (*copy_construct_type)(void const* src, void* dst);
typedef void (*move_assign_type)(void* src, void* dst);
typedef void (*copy_assign_type)(void const* src, void* dst);
typedef void (*destroy_type)(void* dst);
move_construct_type move_construct;
copy_construct_type copy_construct;
move_assign_type move_assign;
copy_assign_type copy_assign;
destroy_type destroy;
};
template <typename Base, typename Derived>
void core_move_construct_function(void* src, void* dst) {
Derived* derived = reinterpret_cast<Derived*>(src);
new (reinterpret_cast<Base*>(dst)) Derived(std::move(*derived));
} // core_move_construct_function
template <typename Base, typename Derived>
void core_copy_construct_function(void const* src, void* dst) {
Derived const* derived = reinterpret_cast<Derived const*>(src);
new (reinterpret_cast<Base*>(dst)) Derived(*derived);
} // core_copy_construct_function
template <typename Derived>
void core_move_assign_function(void* src, void* dst) {
Derived* source = reinterpret_cast<Derived*>(src);
Derived* destination = reinterpret_cast<Derived*>(dst);
*destination = std::move(*source);
} // core_move_assign_function
template <typename Derived>
void core_copy_assign_function(void const* src, void* dst) {
Derived const* source = reinterpret_cast<Derived const*>(src);
Derived* destination = reinterpret_cast<Derived*>(dst);
*destination = *source;
} // core_copy_assign_function
template <typename Derived>
void core_destroy_function(void* dst) {
Derived* d = reinterpret_cast<Derived*>(dst);
d->~Derived();
} // core_destroy_function
template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
std::is_same<Tag, MoveConstructibleTag>::value,
polymorphic_value_vtable::move_construct_type
>::type
build_move_construct_function()
{
return &core_move_construct_function<Base, Derived>;
} // build_move_construct_function
template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
std::is_same<Tag, CopyConstructibleTag>::value,
polymorphic_value_vtable::copy_construct_type
>::type
build_copy_construct_function()
{
return &core_copy_construct_function<Base, Derived>;
} // build_copy_construct_function
template <typename Tag, typename Derived>
typename std::enable_if<
std::is_same<Tag, MoveAssignableTag>::value,
polymorphic_value_vtable::move_assign_type
>::type
build_move_assign_function()
{
return &core_move_assign_function<Derived>;
} // build_move_assign_function
template <typename Tag, typename Derived>
typename std::enable_if<
std::is_same<Tag, CopyAssignableTag>::value,
polymorphic_value_vtable::copy_construct_type
>::type
build_copy_assign_function()
{
return &core_copy_assign_function<Derived>;
} // build_copy_assign_function
template <typename Base, typename Derived,
typename MC, typename CC,
typename MA, typename CA>
polymorphic_value_vtable const& build_vtable() {
static polymorphic_value_vtable const V = {
build_move_construct_function<MC, Base, Derived>(),
build_copy_construct_function<CC, Base, Derived>(),
build_move_assign_function<MA, Derived>(),
build_copy_assign_function<CA, Derived>(),
&core_destroy_function<Derived>
};
return V;
} // build_vtable
The one trick I use here is to let the user configure whether the types he will use in this container can be move constructed, move assigned, ... via capability tags. A number of operations are keyed on these tags and will either be disabled or less efficient if the requested capability
You can create a stack allocator instance (with some max limit of course) and pass that as an argument to your pet_maker function. Then instead of regular new do a placement new on the address provided by the stack allocator.
You can probably also default to new on exceeding max_size of the stack allocator.
One way is to work out, in advance through analysis, how many of each type of object is needed by your program.
Then you can allocate arrays of an appropriate size in advance, as long as you have book-keeping to track the allocation.
For example;
#include <array>
// Ncats, Ndogs, etc are predefined constants specifying the number of cats and dogs
std::array<Cat, Ncats> cats;
std::array<Dog, Ndogs> dogs;
// bookkeeping - track the returned number of cats and dogs
std::size_t Rcats = 0, Rdogs = 0;
Pet *pet_maker()
{
// determine what needs to be returned
if (return_cat)
{
assert(Rcats < Ncats);
return &cats[Rcats++];
}
else if (return_dog)
{
assert(Rdogs < Ndogs);
return &dogs[Rdogs++];
}
else
{
// handle other case somehow
}
}
Of course, the big trade-off in is the requirement to explicitly determine the number of each type of animal in advance - and separately track each type.
However, if you wish to avoid dynamic memory allocation (operator new) then this way - as draconian as it might seem - provides an absolute guarantee. Using operator new explicitly allows the number of objects needed to be determined at run time. Conversely, to avoid using operator new but allow some function to safely access a number of objects it is necessary to predetermine the number of objects.
It depends on the exact use case you have, and what restrictions you are willing to tolerate. For example, if you are OK with re-using the same objects rather than having new copies every time, you could return references to static objects inside the function:
Pet& pet_maker()
{
static Dog dog;
static Cat cat;
//...
if(shouldReturnDog) {
//manipulate dog as necessary
//...
return dog;
}
else
{
//manipulate cat as necessary
//...
return cat;
}
}
This works if the client code accepts that it doesn't own the object returned and that the same physical instances are reused.
There are other tricks possible if this particular set of assumptions is unsuitable.
At some point somebody is going to have to allocate the memory and initialize the objects. If doing them on demand, using the heap via new is taking too long, then why no pre-allocate a number of then in a pool. Then you can initialize each individual object on an as needed basis. The downside is that you might have a bunch of extra objects laying around for a while.
If actually initializing the object is the problem, and not memory allocation, then you can consider keeping a pre-built object around and using the Pototype pattern for quicker initialization.
For best results, memory allocation is problem and initialization time, you can combine both strategies.
You may want to consider using a (Boost) variant. It will require an extra step by the caller, but it might suit your needs:
#include <boost/variant/variant.hpp>
#include <boost/variant/get.hpp>
#include <iostream>
using boost::variant;
using std::cout;
struct Pet {
virtual void print_type() const = 0;
};
struct Cat : Pet {
virtual void print_type() const { cout << "Cat\n"; }
};
struct Dog : Pet {
virtual void print_type() const { cout << "Dog\n"; }
};
using PetVariant = variant<Cat,Dog>;
enum class PetType { cat, dog };
PetVariant make_pet(PetType type)
{
switch (type) {
case PetType::cat: return Cat();
case PetType::dog: return Dog();
}
return {};
}
Pet& get_pet(PetVariant& pet_variant)
{
return apply_visitor([](Pet& pet) -> Pet& { return pet; },pet_variant);
}
int main()
{
PetVariant pet_variant_1 = make_pet(PetType::cat);
PetVariant pet_variant_2 = make_pet(PetType::dog);
Pet& pet1 = get_pet(pet_variant_1);
Pet& pet2 = get_pet(pet_variant_2);
pet1.print_type();
pet2.print_type();
}
Output:
Cat
Dog
For example I have some function pet_maker() that creates and returns a Cat or a Dog as a base Pet. I want to call this function many many times, and do something with the Pet returned.
If you are going to discard the pet immediately after you have done something with it, you can use the technique shown in the following example:
#include<iostream>
#include<utility>
struct Pet {
virtual ~Pet() = default;
virtual void foo() const = 0;
};
struct Cat: Pet {
void foo() const override {
std::cout << "cat" << std::endl;
}
};
struct Dog: Pet {
void foo() const override {
std::cout << "dog" << std::endl;
}
};
template<typename T, typename F>
void factory(F &&f) {
std::forward<F>(f)(T{});
}
int main() {
auto lambda = [](const Pet &pet) { pet.foo(); };
factory<Cat>(lambda);
factory<Dog>(lambda);
}
No allocation required at all. The basic idea is to revert the logic: the factory no longer returns an object. Instead it calls a function providing the right instance as a reference.
The problem with this approach arises if you want to copy and store the object somewhere.
For it is not clear from the question, it's worth to propose also this solution.
Suppose I have this class :
class Component1;
class Component2;
// many different Components
class Component42;
class MyClass
{
public:
MyClass(void) {};
std::list<Component1> component1List;
std::list<Component2> component2List;
// one list by component
std::list<Component42> component42List;
};
I would like to create a function with the following signature:
template<class T> void addElement(T component);
It should do the following:
if component is of type Component1, add it to Component1List
if component is of type Component2, add it to Component2List, etc.
Is it possible? What's a good way to do this?
I can obtain the same behaviour with a function like :
template<class T> void addElement(int componentType, T component);
but I'd rather not have to specify the componentType like this : it's useless information and it open the door to possible errors (if componentType doesn't represent the type of component).
std::tuple to the rescue.
changelog:
use std::decay_t
added the variadic argument form
add_component() now returns a reference to this to allow call-chaining.
#include <iostream>
#include <list>
#include <utility>
#include <type_traits>
#include <tuple>
class Component1 {};
class Component2 {};
struct Component3 {
Component3() {}
};
// many different Components
template<class...ComponentTypes>
class MyClassImpl
{
template<class Component> using list_of = std::list<Component>;
public:
using all_lists_type =
std::tuple<
list_of<ComponentTypes> ...
>;
// add a single component
template<class Component>
MyClassImpl& add_component(Component&& c)
{
list_for<Component>().push_back(std::forward<Component>(c));
return *this;
}
// add any number of components
template<class...Components>
MyClassImpl& add_components(Components&&... c)
{
using expand = int[];
void(expand { 0, (void(add_component(std::forward<Components>(c))), 0)... });
return *this;
}
template<class Component>
auto& list_for()
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
template<class Component>
const auto& list_for() const
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
private:
all_lists_type _lists;
};
using MyClass = MyClassImpl<Component1, Component2, Component3>;
int main()
{
MyClass c;
c.add_component(Component1());
c.add_component(Component2());
const Component3 c3;
c.add_component(c3);
c.add_components(Component1(),
Component2(),
Component3()).add_components(Component3()).add_components(Component1(),
Component2());
std::cout << c.list_for<Component1>().size() << std::endl;
return 0;
}
The most straightforward variant is to simply not use templates but to overload the addElement() function:
void addElement(Component1 element)
{
this->element1List.push_back(element);
}
void addElement(Component2 element)
{
this->element2List.push_back(element);
}
// ... etc
However, this might get tedious if you have many of these (and you don't just have addElement(), I guess). Using a macro to generate the code for each type could still do the job with reasonable effort.
If you really want to use templates, you could use a template function and specialize the template function for each type. Still, this doesn't reduce the amount of code repetition when compared with the above approach. Also, you could still reduce it using macros to generate the code.
However, there's hope for doing this in a generic way. Firstly, let's create a type that holds the list:
template<typename T>
struct ComponentContainer
{
list<T> componentList;
};
Now, the derived class just inherits from this class and uses C++ type system to locate the correct container baseclass:
class MyClass:
ComponentContainer<Component1>,
ComponentContainer<Component2>,
ComponentContainer<Component3>
{
public:
template<typename T>
void addElement(T value)
{
ComponentContainer<T>& container = *this;
container.componentList.push_back(value);
}
}
Notes here:
This uses private inheritance, which is very similar to the containment you originally used.
Even though ComponentContainer is a baseclass, it doesn't have any virtual functions and not even a virtual destructor. Yes, this is dangerous and should be documented clearly. I wouldn't add a virtual destructor though, because of the overhead it has and because it shouldn't be needed.
You could drop the intermediate container altogether and derive from list<T>, too. I didn't because it will make all of list's memberfunctions available in class MyClass (even if not publicly), which might be confusing.
You can't put the addElement() function into the base class template to avoid the template in the derived class. The simple reason is that the different baseclasses are scanned in order for a addElement() function and only then overload resolution is performed. The compiler will only find the addElement() in the first baseclass therefore.
This is a plain C++98 solution, for C++11 I'd look at the type-based tuple lookup solutions suggested by Jens and Richard.
If there are not too many classes you could go with overloading. A template-based solution could be done with type-based lookup for tuples:
class MyClass {
public:
template<typename T> void addElement(T&& x) {
auto& l = std::get<std::list<T>>(lists);
l.insert( std::forward<T>(x) );
}
private:
std::tuple< std::list<Component1>, std::list<Component2> > lists;
};
If you don't know in advance the types you will need storing when instantiating the multi-container an option is to hide the types and using type_index to keep a map of lists:
struct Container {
struct Entry {
void *list;
std::function<void *(void*)> copier;
std::function<void(void *)> deleter;
};
std::map<std::type_index, Entry> entries;
template<typename T>
std::list<T>& list() {
Entry& e = entries[std::type_index(typeid(T))];
if (!e.list) {
e.list = new std::list<T>;
e.deleter = [](void *list){ delete ((std::list<T> *)list); };
e.copier = [](void *list){ return new std::list<T>(*((std::list<T> *)list)); };
}
return *((std::list<T> *)e.list);
}
~Container() {
for (auto& i : entries) i.second.deleter(i.second.list);
}
Container(const Container& other) {
// Not exception safe... se note
for (auto& i : other.entries) {
entries[i.first] = { i.second.copier(i.second.list),
i.second.copier,
i.second.deleter };
}
};
void swap(Container& other) { std::swap(entries, other.entries); }
Container& operator=(const Container& other) {
Container(other).swap(*this);
return *this;
};
Container() { }
};
that can be used as:
Container c;
c.list<int>().push_back(10);
c.list<int>().push_back(20);
c.list<double>().push_back(3.14);
NOTE: the copy constructor as written now is not exception safe because in case a copier throws (because of an out of memory or because a copy constructor of an element inside a list throws) the already allocated lists will not be deallocated.
void addElement(Component1 component) {
componentList1.insert(component);
}
void addElement(Component2 component) {
componentList2.insert(component);
}
Note: I know similar questions to this have been asked on SO before, but I did not find them helpful or very clear.
Second note: For the scope of this project/assignment, I'm trying to avoid third party libraries, such as Boost.
I am trying to see if there is a way I can have a single vector hold multiple types, in each of its indices. For example, say I have the following code sample:
vector<something magical to hold various types> vec;
int x = 3;
string hi = "Hello World";
MyStruct s = {3, "Hi", 4.01};
vec.push_back(x);
vec.push_back(hi);
vec.push_back(s);
I've heard vector<void*> could work, but then it gets tricky with memory allocation and then there is always the possibility that certain portions in nearby memory could be unintentionally overridden if a value inserted into a certain index is larger than expected.
In my actual application, I know what possible types may be inserted into a vector, but these types do not all derive from the same super class, and there is no guarantee that all of these types will be pushed onto the vector or in what order.
Is there a way that I can safely accomplish the objective I demonstrated in my code sample?
Thank you for your time.
The objects hold by the std::vector<T> need to be of a homogenous type. If you need to put objects of different type into one vector you need somehow erase their type and make them all look similar. You could use the moral equivalent of boost::any or boost::variant<...>. The idea of boost::any is to encapsulate a type hierarchy, storing a pointer to the base but pointing to a templatized derived. A very rough and incomplete outline looks something like this:
#include <algorithm>
#include <iostream>
class any
{
private:
struct base {
virtual ~base() {}
virtual base* clone() const = 0;
};
template <typename T>
struct data: base {
data(T const& value): value_(value) {}
base* clone() const { return new data<T>(*this); }
T value_;
};
base* ptr_;
public:
template <typename T> any(T const& value): ptr_(new data<T>(value)) {}
any(any const& other): ptr_(other.ptr_->clone()) {}
any& operator= (any const& other) {
any(other).swap(*this);
return *this;
}
~any() { delete this->ptr_; }
void swap(any& other) { std::swap(this->ptr_, other.ptr_); }
template <typename T>
T& get() {
return dynamic_cast<data<T>&>(*this->ptr_).value_;
}
};
int main()
{
any a0(17);
any a1(3.14);
try { a0.get<double>(); } catch (...) {}
a0 = a1;
std::cout << a0.get<double>() << "\n";
}
As suggested you can use various forms of unions, variants, etc. Depending on what you want to do with your stored objects, external polymorphism could do exactly what you want, if you can define all necessary operations in a base class interface.
Here's an example if all we want to do is print the objects to the console:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class any_type
{
public:
virtual ~any_type() {}
virtual void print() = 0;
};
template <class T>
class concrete_type : public any_type
{
public:
concrete_type(const T& value) : value_(value)
{}
virtual void print()
{
std::cout << value_ << '\n';
}
private:
T value_;
};
int main()
{
std::vector<std::unique_ptr<any_type>> v(2);
v[0].reset(new concrete_type<int>(99));
v[1].reset(new concrete_type<std::string>("Bottles of Beer"));
for(size_t x = 0; x < 2; ++x)
{
v[x]->print();
}
return 0;
}
In order to do that, you'll definitely need a wrapper class to somehow conceal the type information of your objects from the vector.
It's probably also good to have this class throw an exception when you try to get Type-A back when you have previously stored a Type-B into it.
Here is part of the Holder class from one of my projects. You can probably start from here.
Note: due to the use of unrestricted unions, this only works in C++11. More information about this can be found here: What are Unrestricted Unions proposed in C++11?
class Holder {
public:
enum Type {
BOOL,
INT,
STRING,
// Other types you want to store into vector.
};
template<typename T>
Holder (Type type, T val);
~Holder () {
// You want to properly destroy
// union members below that have non-trivial constructors
}
operator bool () const {
if (type_ != BOOL) {
throw SomeException();
}
return impl_.bool_;
}
// Do the same for other operators
// Or maybe use templates?
private:
union Impl {
bool bool_;
int int_;
string string_;
Impl() { new(&string_) string; }
} impl_;
Type type_;
// Other stuff.
};
When I have to extend the behaviour of a class without modifying it, I often use the design pattern visitor. It adds member-like functions without modifying the core of the class it works with.
More or less in the same way, I need to extend a third party class, but mostly with data, not behaviour.
In such cases, I often use a std::map matching the a key MyClass* with a value MyClassExtender. MyClassExtender contains all the additionnal information.
While doing that, I happened to wonder if there are other ways of doing that, maybe more common or more 'best-practice". Should I call this additive class an Extender ?
Is there a name for such a pattern...
Nota Bene: I could have simply aggregated the MyClass* and MyClassExtender in a new class, but I need to access MyClassExtender given a MyClass* really often, so the st::map is really convinient.
Why don't you just subclass the class? Inheritance is the way to extend classes, whether with behavior or state. Unless you just want to associate instances of the class with other data, in which case it's not extending at all, and a std::map is the right answer.
So - create your MyClass object with in the struct with your extension objects:
struct MyClassEx {
MyClassExtension extension;
MyClass object;
};
To make it more robustness for different types - use templates from the example: http://ideone.com/mmfK83
The solution below is inspired by std::shared_ptr/std::make_shared:
template <typename Type>
struct LinkExtension;
template <typename Type>
struct TypeEx {
using Extension = typename LinkExtension<Type>::Type;
alignas(Type) uint8_t objectData[sizeof(Type)];
alignas(Extension) uint8_t extensionData[sizeof(Extension)];
Type* getObject() { return reinterpret_cast<Type*>(objectData); }
const Type* getObject() const { return reinterpret_cast<const Type*>(objectData); }
Extension* getExtension() { return reinterpret_cast<Extension*>(extensionData); }
const Extension* getExtension() const { return reinterpret_cast<const Extension*>(extensionData); }
template <class... Args>
TypeEx(Args&&... args)
{
new (objectData) Type(std::forward<Args>(args)...);
new (extensionData) Extension();
}
~TypeEx()
{
getObject()->~Type();
getExtension()->~Extension();
}
TypeEx(const TypeEx&) = delete;
TypeEx& operator = (const TypeEx&) = delete;
};
And some helper functions:
template <typename Type, class... Args>
Type* createObjectEx(Args&&... args)
{
TypeEx<Type>* retVal = new TypeEx<Type>(std::forward<Args>(args)...);
return retVal->getObject();
}
template <typename Type>
typename LinkExtension<Type>::Type& getObjectEx(Type* obj)
{
static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
TypeEx<Type>* retVal = static_cast<TypeEx<Type>*>((void*)obj);
return *(retVal->getExtension());
}
template <typename Type>
const typename LinkExtension<Type>::Type& getObjectEx(const Type* obj)
{
static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
const TypeEx<Type>* retVal = static_cast<const TypeEx<Type>*>((const void*)obj);
return *(retVal->getExtension());
}
template <typename Type>
void deleteObjectEx(const Type* obj)
{
const TypeEx<Type>* objectEx = static_cast<const TypeEx<Type>*>((const void*)obj);
delete objectEx;
}
And how to link extension to class:
class MyClass {
public:
virtual ~MyClass() = default;
};
struct MyClassExtension {
int a;
int b;
};
template <>
struct LinkExtension<MyClass> {
using Type = MyClassExtension;
};
And proof it works:
void printExtension(MyClass* object);
int main() {
MyClass* object = createObjectEx<MyClass>();
MyClassExtension& extension = getObjectEx(object);
extension.a = 1;
extension.b = 2;
printExtension(object);
deleteObjectEx(object);
TypeEx<MyClass> objectEx;
objectEx.getExtension()->a = 3;
objectEx.getExtension()->b = 4;
printExtension(objectEx.getObject());
}
void printExtension(MyClass* object)
{
MyClassExtension& extension = getObjectEx(object);
std::cout << extension.a << ' ' << extension.b << std::endl;
}
If your compiler does not support variadic templates, the solution is still possible, but requires more hand work to be complete.