I have a Base which has two child classes. bar creates a unique_ptr who's type is Base and attempts to initialize it with one of its child classes.
Certainly I can't downcast without being explicit of the type which I don't want to do. Are there ways around it?
struct Base
{
};
struct A : public Base
{
int val;
};
struct B : public Base
{
int val;
};
struct C : public Base
{
// does not have val
};
void bar(bool x, int value)
{
std::unique_ptr<Base> ptr;
if (x)
{
ptr = std::make_unique<A>();
}
else
{
ptr = std::make_unique<B>();
}
ptr->val = value; // ERROR
}
Types in C++ don't work this way.
If *ptr needs to have val, this must be reflected in the static type of *ptr.
In your case the static type of *ptr is Base. There is no val there.
Data flow analysis may prove that the dynamic type of *ptr always has val, but that's irrelevant. The implementation doesn't do data flow analysis. It only looks at static types.
If you need some descendants of Base to have val and others not, create an intermediate class BaseWithVal and work with that.
You can use a generic lambda or helper function template to avoid code duplication:
void bar(bool x, int value)
{
auto make_ptr = [&]<typename T>(){
auto ptr = std::make_unique<T>();
ptr->val = value;
return ptr;
};
std::unique_ptr<Base> ptr;
if (x)
{
ptr = make_ptr.operator()<A>();
}
else
{
ptr = make_ptr.operator()<B>();
}
}
This requires C++20 for the explicit template parameter on the lambda. Before C++20 a helper function is easier to manage, although the following alternative also works:
template<typename T>
struct type_identity {
using type = T;
};
void bar(bool x, int value)
{
auto make_ptr = [&](auto t){
using T = typename decltype(t)::type;
auto ptr = std::make_unique<T>();
ptr->val = value;
return ptr;
};
std::unique_ptr<Base> ptr;
if (x)
{
ptr = make_ptr(type_identity<A>{});
}
else
{
ptr = make_ptr(type_identity<B>{});
}
}
But as the other answer says, if there is a need to uniformly access a subset of derived classes with a val member, then these should very likely share an intermediate base class with that member. That way you can simply replace std::unique_ptr<Base> with std::unique_ptr<IntermediateBase> and everything will work.
Related
Consider the following classes
class A {
public:
virtual std::string to_string() = 0;
};
class B : public A {
public:
std::string to_string(){
return "B";
}
};
class C : public A {
public:
std::string to_string(){
return "C";
}
};
class D {};
Now I will have a variant std::variant<B*, C*, D*> bcd;The variable can of course, depending on the user input, hold either a variable of type B* or of type C*. At a given time in the program, I want to extract the value to pass to a method taking the superclass A* as an argument. Now, if I do this explicitly like this:
bc = new C();
A* a = std::get<C*>(bc);
It works as expected. But I do not know at this point, which type the inner value of bc will have.
I have tried the following:
Adding A* to the variant and trying to access it with std::get<A*>(bc), which results in a bad variant access.
Creating a visitor pattern to return the type (even though this seems cumbersome for this simple task) like this
class Visitor {
template<typename TType>
TType operator()(TType arg) {
return arg;
}
};
A* a2 = std::visit(visitor, bc);
which produces a no type named ‘type’ in ‘std::conditional_t‘. (I also tried it without templates).
Is there a right / elegant way to do this without having to do something like
if(B* b = std::get_if<B*>(bc))
for every type that I have?
You were close with std::visit, not sure what the error is exactly but I would recommend using a lambda instead of a custom struct:
int main(){
std::variant<B*, C*> bc;
bc = new C();
A* a = std::visit([](auto&& e)->A*{return e;},bc);
a->to_string();
}
The above will compile iff all variants can be casted to A* and is thus safe.
If some of variants are not derived from A*, you could use this longer version:
A* a = std::visit(
[](auto& e) -> A* {
if constexpr (std::is_convertible_v<decltype(e),A*>)
return e;
else
return nullptr;
},
bc);
It will return nullptr if the currently held type cannot be casted to A.
One can hide the ugly lambda (the simple one above can be hidden too) into a global variable template, full example:
template <typename T>
constexpr auto shared_cast = [](auto& e) -> T* {
if constexpr (std::is_convertible_v<decltype(e), T*>)
return e;
else
return nullptr;
};
int main() {
std::variant<B*, C*, D*> bc;
bc = new C();
A* a = std::visit(shared_cast<A>, bc);
if (a != nullptr) a->to_string();
}
Feel free to refactor the whole std::visit expression into a template function:
template <typename T,typename V>
T* visit2(V& variant){
return std::visit(shared_cast<T>,variant);
}
I have the following code that allows me to use an entity component system. However, due to the nature of templates, adding components from a std::vector<HE2_Component*> causes them to be added with the typeID of HE2_Component instead of their most derived form (example at bottom of code). How can I force this to correctly identify component types?
template<typename T,
typename = std::enable_if_t<std::is_base_of_v<HE2_Component, T>>>
void addComponent(T* component)
{
components.insert(std::make_pair(std::type_index(typeid(T)), component));
component->host = this;
}
template<typename CompType,
typename = std::enable_if_t<std::is_base_of_v<HE2_Component, CompType>>>
inline void removeComponent()
{
auto it = components.find(std::type_index(typeid(CompType)));
if (it == components.end())
return;
components.erase(it->first);
}
template<typename CompType,
typename = std::enable_if_t<std::is_base_of_v<HE2_Component, CompType>>>
inline CompType* getComponent()
{
auto it = components.find(std::type_index(typeid(CompType)));
if (it == components.end())
{
throw std::runtime_error("Object does not contain this component!");
return nullptr;
}
return dynamic_cast<CompType*>(it->second);
}
//EXAMPLE HERE
//Setup
HE2_ComponentOwner* obj = new HE2_ComponentOwner();
HE2_ComponentOwner* obj2 = new HE2_ComponentOwner();
class A : virtual public HE2_Component { double f = 0.0; };
class B : virtual public HE2_Component { float b = 0.0f; };
class C : public HE2_Component { int x = 0; };
//Add some components from a vector to obj
std::vector<HE2_Component*> comps = { new A(), new B(), new C() };
for (auto x : comps)
obj->addComponent(x);
//Add some manually to obj2
obj2->addComponent(new A());
obj2->addComponent(new B());
obj2->addComponent(new C());
//This doesn't work
A* a = obj->getComponent<A>();
B* a = obj->getComponent<B>();
C* c = obj->getComponent<C>();
//This does work
A* a = obj2->getComponent<A>();
B* b = obj2->getComponent<B>();
C* c = obj2->getComponent<C>();
You need to do typeid(*component)to get the dynamic type of the pointed-to object, not the declared type of its typename or of the pointer-to-it.
Naturally, typeid(T) will always be T. So, if you pass a less-derived type of pointer T* component, then typeid(T) is not equivalent to typeid(*component).
Also, if you were trying typeid(component), without dereferencing, then you should get the type of the pointer, not the type of what it points to, which shouldn't be correct.
Finally, although this seems already assured in your case, it's worth noting that the objects need to be polymorphic for this to work, i.e. to have at least one virtual member function. Otherwise, the RTTI that this needs won't exist.
I store "instances of different types" with "shared ownership". That's what I currently do:
class Destructible {
public:
virtual ~Destructible() = default;
};
// UGLY
class MyType1 : public Destructible { ... };
class MyTypeN : public Destructible { ... };
class Storage {
std::vector<std::shared_ptr<Destructible>> objects_;
...
}
I'd love to switch to boost::any, removing all these conformances and gaining the ability to store instances of truly any type. Also I like boost::any interface and boost::any_cast.
But my types don't satisfy ValueType requirements, they are not copyable. What is the best (preferably existing) solution for this problem? Something like shared_any_ptr, which captures destructor at creation, has type erasure, reference counter and can do any_cast.
Edit: boost::any allows creation with move, but I'd prefer not to even move and use pointers.
Edit2: I also use make_shared extensively, so something make_shared_any_ptr would come in handy.
This isn't tricky with shared pointers. We can even avoid multiple allocations.
struct any_block {
any_block(any_block const&)=delete;
template<class T>
T* try_get() {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T*>(ptr);
}
template<class T>
T const* try_get() const {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T const*>(ptr);
}
~any_block() {
cleanup();
}
protected:
void cleanup(){
if (dtor) dtor(this);
dtor=0;
}
any_block() {}
std::type_info const* info = nullptr;
void* ptr = nullptr;
void(*dtor)(any_block*) = nullptr;
};
template<class T>
struct any_block_made:any_block {
std::aligned_storage_t<sizeof(T), alignof(T)> data;
any_block_made() {}
~any_block_made() {}
T* get_unsafe() {
return static_cast<T*>((void*)&data);
}
template<class...Args>
void emplace(Args&&...args) {
ptr = ::new((void*)get_unsafe()) T(std::forward<Args>(args)...);
info = &typeid(T);
dtor = [](any_block* self){
static_cast<any_block_made<T>*>(self)->get_unsafe()->~T();
};
}
};
template<class D>
struct any_block_dtor:any_block {
std::aligned_storage_t<sizeof(D), alignof(D)> dtor_data;
any_block_dtor() {}
~any_block_dtor() {
cleanup();
if (info) dtor_unsafe()->~D();
}
D* dtor_unsafe() {
return static_cast<D*>((void*)&dtor_data);
}
template<class T, class D0>
void init(T* t, D0&& d) {
::new( (void*)dtor_unsafe() ) D(std::forward<D0>(d));
info = &typeid(T);
ptr = t;
dtor = [](any_block* s) {
auto* self = static_cast<any_block_dtor<D>*>(s);
(*self->dtor_unsafe())( static_cast<T*>(self->ptr) );
};
}
};
using any_ptr = std::shared_ptr<any_block>;
template<class T, class...Args>
any_ptr
make_any_ptr(Args&&...args) {
auto r = std::make_shared<any_block_made<T>>();
if (!r) return nullptr;
r->emplace(std::forward<Args>(args)...);
return r;
}
template<class T, class D=std::default_delete<T>>
any_ptr wrap_any_ptr( T* t, D&& d = {} ) {
auto r = std::make_shared<any_block_dtor<std::decay_t<D>>>();
if (!r) return nullptr;
r->init( t, std::forward<D>(d) );
return r;
}
you'd have to implement any_cast, but with try_get<T> it should be easy.
There may be some corner cases like const T that the above doesn't handle.
template<class T>
std::shared_ptr<T>
crystalize_any_ptr( any_ptr ptr ) {
if (!ptr) return nullptr;
T* pt = ptr->try_get<T>();
if (!pt) return nullptr;
return {pt, ptr}; // aliasing constructor
}
This lets you take a any_ptr and turn it into a shared_ptr<T> if the types match without copying anything.
live example.
You'll notice how similar any_block_made and any_block_dtor is. I believe that this is why at least one major shared_ptr in a std library reuses the spot the deleter lives in for make_shared itself.
I could probably do similar, and reduce binary size here. In addition, the T/D parameter of any_block_made and any_block_dtor is really just about how big and aligned the block of memory we play with is, and what exactly type erasued helper I store in the dtor pointer in the parent. A compiler/linker with COMDAT folding (MSVC or GOLD) may eliminate the binary bloat here, but with a bit of care I could do it myself.
I have a templated-method where the return-type is will be the result of a reinterpret_cast<>()-call.
class A {
void *_ptr;
public:
template<typename T>
T buffer() { return reinterpret_cast<T>(_ptr); }
};
This way makes me use the <>-syntax when calling this function:
A a;
auto b = a.buffer<double *>();
I'd prefer to call this method without the template arguments and let the compiler deduce the return type, based on the variable-type.
A a;
double *out = a.buffer();
Is this possible with return-type deduction?
I tried using auto, the->-operand and the trailing return type syntax.
auto buffer() -> decltype(reinterpret_cast<T>(_ptr)) const
{ return reinterpret_cast<T>(_ptr); }
but it still doesn't work.
Is there any way doing this, in C++11?
Yes, but only via a proxy type having a conversion function template:
struct BufferProxy {
void* ptr;
template<class T> operator T*() { return reinterpret_cast<T*>(ptr); }
};
BufferProxy buffer() { return BufferProxy{_ptr}; }
Example.
Note that users who have become familiar with the use of auto for return type deduction are likely to become confused by this technique:
auto out = a.buffer(); // out is BufferProxy
auto* out = a.buffer(); // fails to compile; can't deduce 'auto*' from 'a.A::buffer()'
Up until C++17, you can prevent auto out = a.buffer(); from compiling by giving BufferProxy a deleted copy constructor (and perhaps returning it by aggregate construction: return {_ptr};), but the user could still use auto&& and from C++17 guaranteed copy elision will make the auto form work again.
You may want a class something like the following. This would seem to offer most of what you want to do.
One issue I was wondering about was how to determine if a pointer stored into the class was the same type or not. So I thought it would be best to add an additional method to check the typeid() using the hash_code() method.
So the class I came up with using the operator idea of #ecatmur in his/her answer:
class A {
void *_ptr;
size_t _ptrHash;
public:
template<typename T> operator T*() { return reinterpret_cast<T *>(_ptr); }
template<typename T>
void SetPtr(T *p) { _ptr = p; _ptrHash = typeid(*p).hash_code(); }
template<typename T> bool operator == (T *p) { return p && typeid(*p).hash_code() == _ptrHash /* && p == _ptr */; }
};
The equality operator could either check only the type as above or if you uncomment the additional check, also check for value of the pointer. You probably just want to check for the type.
A simple demo function that I used to test this out was as follows:
void funky1() {
A a;
double ddd[50] = { 0.0 };
ddd[0] = 5.0; ddd[2] = 7.0;
a.SetPtr(&ddd[0]);
double *p = a;
bool bb = a == p;
long lll[50] = { 0 };
lll[0] = 5; lll[2] = 7;
long *q = a;
bb = a == q;
a.SetPtr(&lll[0]);
q = a;
bb = a == q;
}
I stepped through this with the debugger, Visual Studio 2013, and it looked like it worked like a champ.
I guess this answer is the most elegant.
Anyway, you can also let the class initializes your pointer as it follows:
class A {
void *_ptr;
public:
template<typename T>
void buffer(T **t) { *t = reinterpret_cast<T*>(_ptr); }
};
int main() {
A a;
double *b;
a.buffer(&b);
}
This way the type is deduced from the parameter list and you have not to explicitly specify it.
I need one C wrapper for any C++ vector that can be passed to a function that expects some specific type of vector. Like my below C wrapper for OpenCV's BRISK"
void cv_BRISK_generateKernel(BRISK* self, vector_float* radiusList,
vector_int* numberList, float dMax, float dMin, vector_int* indexChange) {
self->generateKernel(*radiusList, *numberList, dMax, dMin, *indexChange);
}
the vector_int* and vector_float* are typedef's as below
typedef vector<int> vector_int;
typedef vector<float> vector_float;
These are the vector wrappers I have so far, they work, but I would like to know if there is a way to make just one wrapper for all the vector types. It would have to go in an Extern C..so it can't be a template. but instead of having the below wrappers I would like to make just one wrapper that can be passed to a function expecting a vector_float*(typedeffor vector<float>) or a vector_KeyPoint*(typedef for vector<KeyPoint>) or a vector_int*(typedef for vector<int>) etc. I know about template classes but I can't use them here because it has to go in an extern C {}
vector_float* std_create_vectorf() {
return new vector<float>;
}
vector_int* std_create_vector() {
return new vector<int>;
}
vector_char* std_create_vectorc() {
return new vector<char>;
}
Here is my idealized wrapper If someone can help me figure out how to make this happen I would appreciate it
vector_any_vec* std_create_vectorany() {
return new vector<anyvector>;
}
How about wrapping this up in an interface using run-time polymorphism? You sacrifice a bit of type safety but it should achieve what you need.
enum stored_type
{
st_int,
st_float,
st_char
};
struct IGeneralVector
{
virtual stored_type get_type() = 0;
virtual void* get_data() = 0;
virtual ~IGeneralVector();
};
class VectorFloatHolder : public IGeneralVector
{
std::vector<float>* data;
public:
VectorFloatHolder(std::vector<float>* in) : data(in)
{}
virtual stored_type get_type() override
{
return st_float;
}
virtual void* get_data() override
{
return reinterpret_cast<void *>(data);
}
virtual ~VectorFloatHolder()
{
delete data;
}
};
IGeneralVector* std_create_vectorf()
{
return new VectorFloatHolder(new std::vector<float>);
}
Update
After reading your comment I have a slightly better idea of what you're trying to achieve. But I'm not sure whether it's possible to do exactly what you want as I'm not sure of any other implementation constraints you have. Here's another way to go this time using type erasure.
class ObjectHolder
{
struct base_type
{
virtual ~base_type() {}
};
template<typename T>
struct object_type : base_type
{
object_type(const T& t) : object(t) {}
T object;
};
std::unique_ptr<base_type> data;
public:
template<typename T>
VectorHolder(T t) : data(new object_type<T>(t))
{
}
template<typename T>
T GetData()
{
object_type<T>* val = static_cast<object_type<T>*>(data.get());
return val->object;
}
};
template<typename T>
ObjectHolder* std_create_vector()
{
return new VectorHolder(new std::vector<T>);
}
int main()
{
ObjectHolder* vh = std_create_vector < std::vector<float>>();
// then later on you can get back the original type via:
std::vector<float>* fp = vh->GetData<std::vector<float>*>();
}