Let's suppose that we have 3 classes:
class A
{
public:
int a;
virtual ~A() = default;
};
class B : public A
{
public:
int b;
};
class C : public A
{
public:
int c;
};
And a vector that contains polymorphic objects derived from A
std::vector<A*> objects;
I want to have a template method, that will return me an object from vector of type gived in template, if that object exists, but I don't know how to write it..
This not work:
template<typename ComponentType>
ComponentType * GetComponent()
{
bool pred = std::find_if(objects.begin(), objects.end(),
[=](A * obj)
{
return
dynamic_cast<ComponentType*>(obj) != nullptr;
});
return pred != objects.end();
}
I know that it could be done with that code:
template<typename ComponentType>
ComponentType * GetComponent()
{
for (item : objects)
{
auto casted = dynamic_cast<ComponentType*>(item);
if (casted)
return casted;
}
return nullptr;
}
But I want to use lambda.
It does not work at least because std::find_if returns an iterator, not a bool.
You might change your solution to fix it and return either a pointer to the element or nullptr if there is no such element:
template<typename ComponentType>
ComponentType * GetComponent()
{
auto it = std::find_if(objects.begin(), objects.end(),
[](A * obj)
{
return dynamic_cast<ComponentType*>(obj) != nullptr;
});
return it != objects.end()
? dynamic_cast<ComponentType*>(*it)
: nullptr;
}
Note, that it is better to avoid dynamic_cast at all if it is possible. For example, you might add a virtual method to A class which identifies an object somehow, then override it in B and C and use to find a requested object.
Related
Let say I have this class design
class A {};
class B : public A {};
class C : public A {};
and a container of A like this
std::list<A *> elements;
Now what I want to achieve is iterate through all B objects in my container, or, in another time, iterate through all C objects.
The classic way would be
for (auto it = elements.begin(); it != elements.end(); ++it) {
B * b = dynamic_cast<B *>(*it);
if (b) {
// do stuff
}
}
One idea that comes to my mind is creating an iterator class derived from standard that filters but it would be difficult.
No limits on the c++ language level (c++20 may be ok as well but it would be great to see C++11 replies).
Plain c++ and stl please (I know boost has some foreach if construct but).
A possible c++20 implementation using range
#include <iostream>
#include <list>
#include <ranges>
struct A {
virtual ~A() = default;
};
struct B : public A {
void foo() const { std::cout << "B\n"; }
};
struct C : public A {};
int main() {
std::list<A *> demo{new A{}, new B{}, new C{}, new B{}};
auto is_B = [](const A *p) { return dynamic_cast<const B *>(p) != nullptr; };
auto get_B_const = [](const A *p) { return dynamic_cast<const B *>(p); };
for (auto p_B :
demo | std::views::filter(is_B) | std::views::transform(get_B_const)) {
p_B->foo();
}
// demo destruction with delete not shown
}
Prints:
B
B
Demo: https://godbolt.org/z/6oP8hj
Note: if performance matter you can avoid using dynamic_cast two times by
auto get_B_const = [](const A *p) {
assert(dynamic_cast<const B *>(p));
return static_cast<const B *>(p);
};
I can add 2 cents: normally this smells like a design flaw ( sure there are exceptions ), this problem of "heteroganeous container", does not have a "good" solution so far. Something I have seen in th wilds is that on top of std:vector<A*> va with all elements, you may maintain another vector only with "B*" objects, std::vector<B*> vb, when it´s time to iterate go for vb when it´s time to delete go for va
One of the possible solutions without dynamic_cast. But care should be taken to state the correct type in derived class constructors.
And I would recommend to use std::unique_ptr if the list actually stores the class objects.
class Base
{
public:
enum class Type
{
A,
B,
C
};
Base() = delete;
virtual ~Base() = default;
Type type() const { return _type; }
protected:
Base(Type type) : _type{type} {}
private:
Type _type;
};
class A : public Base
{
public:
A() : Base{Base::Type::A} {}
};
class B : public Base
{
public:
B() : Base{Base::Type::B} {}
};
class C : public Base
{
public:
C() : Base{Base::Type::C} {}
};
void function()
{
std::list<std::unique_ptr<Base>> list;
list.emplace_back(std::make_unique<A>());
list.emplace_back(std::make_unique<B>());
list.emplace_back(std::make_unique<C>());
// use non-const iterators if you intend to modify the object
std::for_each(std::cbegin(list), std::cend(list),
[](const auto &item)
{
switch (item->type())
{
case Base::Type::B:
{
assert(dynamic_cast<B*>(item.get()));
const auto &b = static_cast<B*>(item.get());
// do staff with b
break;
}
default:
return;
}
});
}
I think in C++11 the way you described is as close as it gets, but I may be wrong on this. C++17 greatly extended the algorithms library, so you could use std::for_each.
To demonstrate this, let's give the classes a little bit of functionality and create a vector (or list) of instances:
class A {
public:
virtual std::string name() const = 0;
};
class B : public A {
public:
virtual std::string name() const override {
return "Class B";
}
};
class C : public A {
public:
virtual std::string name() const override {
return "Class C";
}
};
int main()
{
std::vector<A*> vec { new B(), new B(), new C(), new C(), new B() };
}
Now using for_each, you could re-write your loop:
std::for_each(std::begin(vec), std::end(vec), [](const A* val) {
auto B* b = dynamic_cast<B*>(val);
if (b)
std::cout << b->name() << std::endl;
});
Unfortunately, there is no builtin filter for any of the algorithms. You could, however, implement something like for_each_if:
template<typename Iterator, typename Predicate, typename Operation> void
for_each_if(Iterator begin, Iterator end, Predicate pred, Operation op) {
std::for_each(begin, end, [&](const auto p) {
if (pred(p))
op(p);
});
}
And use it like this:
for_each_if(std::begin(vec), std::end(vec),
[](A* val) { return dynamic_cast<B*>(val) != nullptr; },
[](const A* val) {
std::cout << val->name() << std::endl;
}
);
Or for your specific case, you could specialize the implementation even more:
template<typename T, typename Iterator, typename Operation> void
dynamic_for_each(Iterator begin, Iterator end, Operation op) {
std::for_each(begin, end, [&](auto p) {
auto tp = dynamic_cast<T>(p);
if (tp)
op(tp);
});
}
and use it like so:
dynamic_for_each<B*>(std::begin(vec), std::end(vec), [](const B* val) {
std::cout << val->name() << std::endl;
});
All three implementations print the same output:
Class B
Class B
Class B
You do not need to cast if you got the design right:
struct A {
virtual void doSomethingWithB() = 0;
virtual ~A() = default;
};
struct B : A {
void doSomethingWithB() override {
// do somehting
}
};
struct C : A {
void doSomethingWithB() override {
// do nothing !
}
};
Then your loop is simply:
for (auto elem : elements) {
elem->doSomethingWithB();
}
I dont know if it's possible or not, but i would like to know if it's.
Let say I have a vector of base components like this:
std::vector<std::unique_ptr<Component>> m_components;
To Add to this components vector I have an Add() Method which looks like this:
template<class T>
bool Add() {
static_assert(std::is_base_of<Component, T>::value, " T must be derived from Component");
if (!DuplicatesExist<T>()) {
m_components.emplace_back(std::make_unique<T>(*this));
return true;
}
return false;
}
So if I wanted to add an Input component to the vector of components, which is derived from Component I would do this:
container.Add<Input>();
So lets say I wanted this Input Component because I would like to use the non-derived methods inside of Input. I would like to do something along these lines: container.Get<Input>();
template<class T>
std::unique_ptr<T> &Get() {
static_assert(std::is_base_of<Component, T>::value, " it have to be derived from component");
auto& index = std::find_if(m_components.begin(), m_components.end(),
[](auto &C) -> bool {
// what to do here??
if(std::is_same_v<T, C>)
return true;
});
return index;
}
unfortunately that is not possible :(
Is there a way to do this?
Cheers
The answer depends on whether you want simplest solution or the most efficient.
Simplest would be calling dynamic_cast. Assuming component is of type Component* and T is possible derived, dynamic_cast<T*>(component) would return non-nullptr T* if it is T*, or nullptr otherwise.
The most efficient is to replace your vector with Boost.PolyCollection container base_collection.
There are some options in between. One would be adding a field or a virtual method to base class that would return different values for derived classes. Then if you know the component is of type T, you cast static_cast a pointer or a reference, like static_cast<T&>(component). Contrary intuitive, such ad-hoc implementation of dynamic_cast may perform faster than generic dynamic_cast<T*>(component).
First I have some remarks:
if (!DuplicatesExist<T>())
What do you want to check? If your pointer is an duplicate as it points to the same instance? If so, you can directly use a std::set instead of a vector!
m_components.emplace_back(std::make_unique<T>(*this));
This will generate a new object as a copy from the object "this". So you will get always a new object and a pointer which contains the address of that one which is never equal to any other one! Is this your intention?
And if in this case m_components is a member of the derived class itself, do we want to get a full copy of all elements? Something is very mysterious here :-)
OK, but back to the question:
One solution: Use your own handcrafted "tag" for each type. This code is a bit large and not very convenient:
class Base
{
public:
enum Id { ONE, TWO };
virtual Id GetId() const = 0;
};
class A: public Base
{
public:
static constexpr Id id = ONE;
Id GetId() const override { return id; }
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B: public Base
{
public:
static constexpr Id id = TWO;
Id GetId() const override { return id; }
void BFunc() { std::cout << "B-Func" << std::endl; }
};
template < typename T, typename CONTAINER >
T* Get( CONTAINER &objects )
{
auto it = std::find_if( objects.begin(), objects.end(), []( std::unique_ptr<Base>& ptr ) { return ptr->GetId() == T::id; } );
if ( it != objects.end() ) return static_cast<T*>( it->get() );
return nullptr;
}
int main()
{
std::vector< std::unique_ptr<Base> > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
auto ptra = Get< A >( objects );
if ( ptra ) ptra->AFunc();
auto ptrb = Get< B >( objects );
if ( ptrb ) ptrb->BFunc();
}
Or you use dynamic_cast which needs at minimum one virtual function in your base class and have RTTI enabled for your compiler which is typically not possible on small embedded devices!
class Base
{
virtual void SomeFunc() {};
};
class A: public Base
{
public:
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B: public Base
{
public:
void BFunc() { std::cout << "B-Func" << std::endl; }
};
template < typename T, typename CONTAINER >
T* Get( CONTAINER &objects )
{
T* ret;
for ( auto& ptr: objects )
{
Base* base = ptr.get();
ret = dynamic_cast<T*>( base );
if ( ret ) return ret;
}
return nullptr;
}
int main()
{
std::vector< std::unique_ptr<Base> > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
auto ptra = Get< A >( objects );
if ( ptra ) ptra->AFunc();
auto ptrb = Get< B >( objects );
if ( ptrb ) ptrb->BFunc();
}
or use std::variant
class A
{
public:
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B
{
public:
void BFunc() { std::cout << "B-Func" << std::endl; }
};
using VAR_T = std::variant< std::unique_ptr<A>, std::unique_ptr<B> >;
int main()
{
std::vector< VAR_T > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
for ( auto& ptr: objects )
{
std::visit( []( auto& lptr )
{
using T = std::decay_t<decltype(lptr)>;
if constexpr ( std::is_same< T, std::unique_ptr<A> >::value )
{
lptr->AFunc();
}
else if constexpr ( std::is_same< T, std::unique_ptr<B> >::value )
{
lptr->BFunc();
}
}
, ptr );
}
}
The last one seems to be the easiest way for me. It comes without much additional cost. Only the "tag" is stored inside the variant, but as the variant containing only different pointers of same size, there is no additional cost for size of the variant here. And you can still use it on small embedded devices, as you do not need RTTI. In addition, you do not need a virtual function and you also do not need to derive from a base class. So it is highly flexible!
I'm building a circular linked list, and I would like to know if the do_remove method is well defined. When I run the program, it shows me that it is, however, I'm still a bit confused why I don't need a virtual destrutor in this case.
Is the virtual destructor only needed when I want to destroy a derived class through it's base pointer?
Does this mean that by downcasting a base class to a derived class, and then calling the derived class destructor, it will always call the base class destructor?
Can there be possible leaks in the do_remove method?
PS: I need the object creation and destruction to be a two step process - allocation / call to constructor / call to destructor / deallocation; that's why I'm using ::operator new for the time being.
Here's a self contained example of the code I'm writing:
#include <iostream>
struct NodeBase {
NodeBase * previous;
NodeBase * next;
NodeBase() noexcept
: previous(this)
, next(this) {
}
NodeBase(NodeBase * const previous, NodeBase * const next) noexcept
: previous(previous)
, next(next) {
}
~NodeBase() {
std::puts("~NodeBase()");
}
};
template <typename TYPE>
struct Node : NodeBase {
TYPE data;
template <typename ...ARGUMENTS>
Node(NodeBase * const previous, NodeBase * const next, ARGUMENTS && ...arguments)
: NodeBase(previous, next)
, data(std::forward<ARGUMENTS>(arguments)...) {
previous->next = this;
next->previous = this;
}
~Node() {
std::puts("~Node()");
}
};
template <typename TYPE>
class List {
using Node = Node<TYPE>;
int64_t this_length;
NodeBase this_sentinel;
Node * as_node(NodeBase * const input) noexcept {
return static_cast<Node * const>(input);
}
Node const * as_node(NodeBase const * const input) const noexcept {
return static_cast<Node const * const>(input);
}
template <typename ...ARGUMENTS>
List & do_insert(NodeBase * const node, ARGUMENTS && ...arguments) {
void * const address = ::operator new(sizeof(Node));
try {
new (address) Node(node->previous, node, std::forward<ARGUMENTS>(arguments)...);
++this_length;
return *this;
} catch (...) {
::operator delete(address);
throw;
}
}
// Is this method well defined?
List & do_remove(NodeBase * input) noexcept {
Node * const node = as_node(input);
input->previous->next = input->next;
input->next->previous = input->previous;
node->~Node();
::operator delete(node);
--this_length;
return *this;
}
public:
List()
: this_length(0)
, this_sentinel() {
}
~List() {
std::puts("~List()");
while (this_length) {
pop();
}
}
TYPE & head() noexcept {
return as_node(this_sentinel.next)->data;
}
TYPE const & head() const noexcept {
return as_node(this_sentinel.next)->data;
}
TYPE & last() noexcept {
return as_node(this_sentinel.previous)->data;
}
TYPE const & last() const noexcept {
return as_node(this_sentinel.previous)->data;
}
template <typename ...ARGUMENTS>
List & push(ARGUMENTS && ...arguments) {
return do_insert(this_sentinel.next, std::forward<ARGUMENTS>(arguments)...);
}
List & pop() noexcept {
return do_remove(this_sentinel.next);
}
};
int main() {
List<int> list;
list.push(5).push(7).push(3);
std::cout << list.head() << std::endl;
std::cout << list.last() << std::endl;
return 0;
}
Is the virtual destructor only needed when I want to destroy a derived
class through it's base pointer?
Yes. Only if you delete object using base pointer (or if you manage it using unique_ptr to base) you need virtual destructor in base. Other cases like deleting pointer to most derived type or managing with 'shared_ptr' to base do not need virtual destructor.
Does this mean that by downcasting a base class to a derived class,
and then calling the derived class destructor, it will always call the
base class destructor?
Yes. Destructor of derived class is required to call destructors of (base and member) sub-objects.
Can there be possible leaks in the do_remove method?
No, if the destructor of TYPE does not leak. For simplicity it would be better to use ordinary delete expression
delete node;
instead of writing out what it is anyway required to do
node->~Node();
::operator delete(node);
I'm building an Entity-Component system using template metaprogramming. I keep getting either Cannot convert from [base type] to [type user requested]& or Cannot convert NullComponent to [type user requested]& errors:
class Entity {
public:
Entity() = default;
~Entity() = default;
template<typename C, typename... Args>
void AddComponent(Args&&... args);
template<typename C>
C& GetComponent();
protected:
private:
//...add/get helper methods here...
unsigned int _id;
std::vector<std::unique_ptr<IComponent>> _components;
};
template<typename C>
C& Entity::GetComponent() {
for(auto c : _components) {
if(std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c; //<-- error here
}
}
return NullComponent(); //<-- and here
}
EDIT
These options seem to work for now.
template<typename C>
const C& Entity::GetComponent() const {
for(auto& uc : _components) {
auto* c = dynamic_cast<C*>(uc.get());
if(c && std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c;
}
}
throw std::runtime_error(std::string("Component not available."));
}
OR
class Entity {
public:
//same as before...
protected:
private:
//same as before...
a2de::NullComponent _null_component;
};
template<typename C>
const C& Entity::GetComponent() const {
for(auto& uc : _components) {
auto* c = dynamic_cast<C*>(uc.get());
if(c && std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c;
}
}
return _null_component;
}
At least three things:
In GetComponent() you iterate over unique_ptr elements and compare their type (always std::unique_ptr<IComponent>) with something else in the std::is_same. You probably don't want that.
You are returning a reference to a temporary in the final return, it seems.
return *c needs a dynamic_cast unless C == IComponent.
EDIT
Also:
std::is_base_of makes no sense with references. Even with class NullComponent : IComponent {};, you would still get std::is_base_of<IComponent&, NullComponent&>::value == false.
And you do not check for nullptr
In the end, it seems to me that you should replace your for loop with
for(auto& component : _components) {
auto* c = dynamic_cast<C*>(component.get());
if (c)
{
return *c;
}
}
At a high level, from what I can figure out, the return type cannot be used to define the template type. The parameter list can be used to define the template type.
So, for example, this might work -
template<typename C>
void Entity::GetComponent(C *obj) {
for(auto c : _components) {
if(std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
obj = c; //<-- error here
return;
}
}
obj = NULL;
return; //<-- and here
}
Hope this helps.
In a certain situation I have an std::vector of pointers to ClassA. Another class, ClassB, inherits from ClassA. At some point I know this vector only contains pointers to instances of ClassB. I iterate over the vector with a foreach loop, after which I cast the object to ClassB.
Currently this looks similar to this:
Class ClassA
{
}
Class ClassB : public ClassA
{
public:
void DoSomething();
}
std::vector<ClassA*> vecA;
void iterate()
{
for(ClassA* obj : vecA)
{
((ClassB*)obj)->DoSomething();
}
}
I wonder if I can directly cast the object in the foreach loop. This would be shorter, and would not require me to cast the object every time I want to use it. I am looking for something that looks like this:
void iterate()
{
for((ClassB*)ClassA* obj : vecA)
{
obj->DoSomething();
}
}
Is this possible? And if it is not, what would be an easy way to 'save' the cast, if I want to use the casted object more than one time?
Thanks in advance.
void iterate() {
for(ClassA* obj_ : vecA) {
Assert(dynamic_cast<ClassB*>(obj_)); // debug build sanity check
auto* obj = static_cast<ClassB*>(obj_);
obj->DoSomething();
}
}
How about a container wrapper? Let's look at the usage first:
void iterate()
{
for (ClassB* obj : cast_range<ClassB*>(vecA))
{
obj->DoSomething();
}
}
And here is one possible implementation:
template<typename Target, typename Underlying>
struct cast_iterator_t
{
Underlying it;
Target operator*() const
{
return (Target) *it;
}
bool operator==(cast_iterator_t that) const
{
return it == that.it;
}
bool operator!=(cast_iterator_t that) const
{
return it != that.it;
}
cast_iterator_t& operator++()
{
++it;
return *this;
}
cast_iterator_t operator++(int)
{
cast_iterator_t old(*this);
++*this;
return old;
}
};
template<typename Target, typename Underlying>
cast_iterator_t<Target, Underlying> cast_iterator(Underlying it)
{
return {it};
}
template<typename Target, typename Range>
struct cast_range_t
{
Range& range;
decltype(cast_iterator<Target>(std::begin(range))) begin()
{
return cast_iterator<Target>(std::begin(range));
}
decltype(cast_iterator<Target>(std::end(range))) end()
{
return cast_iterator<Target>(std::end(range));
}
};
template<typename Target, typename Range>
cast_range_t<Target, Range> cast_range(Range& range)
{
return {range};
}