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);
}
Related
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.
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 am trying to create my own boost::adaptors::transformed.
Here is the related boost code.
Here is its usage (modified from a SO answer by LogicStuff):-
C funcPointer(B& b){
//"funcPointer" is function convert from "B" to "C"
return instance-of-C
}
MyArray<B> test; //<-- any type, must already have begin() & end()
for(C c : test | boost::adaptor::transformed(funcPointer)) {
//... something ....
}
The result will be the same as :-
for(auto b : test) {
C c = funcPointer(b);
//... something ...
}
My Attempt
I created CollectAdapter that aim to work like boost::adaptor::transformed.
It works OK in most common cases.
Here is the full demo and back up. (same as below code)
The problematic part is CollectAdapter - the core of my library.
I don't know whether I should cache the collection_ by-pointer or by-value.
CollectAdapter encapsulates underlying collection_ (e.g. pointer to std::vector<>) :-
template<class COLLECTION,class ADAPTER>class CollectAdapter{
using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
COLLECTION* collection_; //<---- #1 problem? should cache by value?
ADAPTER adapter_; //<---- = func1 (or func2)
public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
collection_=&collection;
adapter_=adapter;
}
public: auto begin(){
return IteratorAdapter<
decltype(std::declval<COLLECTION>().begin()),
decltype(adapter_)>
(collection_->begin(),adapter_);
}
public: auto end(){ ..... }
};
IteratorAdapter (used above) encapsulates underlying iterator, change behavior of operator* :-
template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
ADAPTER adapter_;
public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
ITERATORT(underlying),
adapter_(adapter)
{ }
public: auto operator*(){
return adapter_(ITERATORT::operator*());
}
};
CollectAdapterWidget (used below) is just a helper class to construct CollectAdapter-instance.
It can be used like:-
int func1(int i){ return i+10; }
int main(){
std::vector<int> test; test.push_back(5);
for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
//^ create "CollectAdapter<std::vector<int>,func1>" instance
//here, b=5+10=15
}
}
Problem
The above code works OK in most cases, except when COLLECTION is a temporary object.
More specifically, dangling pointer potentially occurs when I create adapter of adapter of adapter ....
int func1(int i){ return i+10; }
int func2(int i){ return i+100; }
template<class T> auto utilityAdapter(const T& t){
auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
//"adapter12.collection_" point to "adapter1"
return adapter12;
//end of scope, "adapter1" is deleted
//"adapter12.collection_" will be dangling pointer
}
int main(){
std::vector<int> test;
test.push_back(5);
for(auto b:utilityAdapter(test)){
std::cout<< b<<std::endl; //should 5+10+100 = 115
}
}
This will cause run time error. Here is the dangling-pointer demo.
In the real usage, if the interface is more awesome, e.g. use | operator, the bug will be even harder to be detected :-
//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer
Question
How to improve my library to fix this error while keeping performance & robustness & maintainablilty near the same level?
In other words, how to cache data or pointer of COLLECTION (that can be adapter or real data-structure) elegantly?
Alternatively, if it is easier to answer by coding from scratch (than modifying my code), go for it. :)
My workarounds
The current code caches by pointer.
The main idea of workarounds is to cache by value instead.
Workaround 1 (always "by value")
Let adapter cache the value of COLLECTION.
Here is the main change:-
COLLECTION collection_; //<------ #1
//changed from .... COLLECTION* collection_;
Disadvantage:-
Whole data-structure (e.g. std::vector) will be value-copied - waste resource.
(when use for std::vector directly)
Workaround 2 (two versions of library, best?)
I will create 2 versions of the library - AdapterValue and AdapterPointer.
I have to create related classes (Widget,AdapterIterator,etc.) as well.
AdapterValue - by value. (designed for utilityAdapter())
AdapterPointer - by pointer. (designed for std::vector)
Disadvantage:-
Duplicate code a lot = low maintainability
Users (coders) have to be very conscious about which one to pick = low robustness
Workaround 3 (detect type)
I may use template specialization that do this :-
If( COLLECTION is an "CollectAdapter" ){ by value }
Else{ by pointer }
Disadvantage:-
Not cooperate well between many adapter classes.
They have to recognize each other : recognized = should cache by value.
Sorry for very long post.
I personally would go with template specialisation – however, not specialise the original template, but a nested class instead:
template<typename Collection, typename Adapter>
class CollectAdapter
{
template<typename C>
class ObjectKeeper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C, typename A>
class ObjectKeeper <CollectAdapter<C, A>>
{
CollectAdapter<C, A> object;
public:
CollectAdapter<C, A>* operator*() { return &object; };
CollectAdapter<C, A>* operator->() { return &object; };
};
ObjectKeeper<Collection> keeper;
// now use *keeper or keeper-> wherever needed
};
The outer class then covers both cases by just always using pointers while the nested class hides the differences away.
Sure, incomplete (you yet need to add appropriate constructors, for instance, both to outer and inner class), but it should give you the idea...
You might even allow the user to select if she/he wants to copy:
template<typename Collection, typename Adapter, bool IsAlwaysCopy = false>
class CollectAdapter
{
template<typename C, bool IsCopy>
class ObjectWrapper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C>
class ObjectWrapper<C, true>
{
C object;
public:
C* operator*() { return &object; };
C* operator->() { return &object; };
};
// avoiding code duplication...
template<typename C, bool IsCopy>
class ObjectKeeper : public ObjectWrapper<C, IsCopy>
{ };
template<typename C, typename A, bool IsCopy>
class ObjectKeeper <CollectAdapter<C, A>, IsCopy>
: public ObjectWrapper<CollectAdapter<C, A>, true>
{ };
ObjectKeeper<Collection> keeper;
};
In my indexed_view I store the value of the collection if it is an rvalue, and store a reference if it is an lvalue. You could do the same here: overload your operator| for both rvalues and lvalues.
template<typename Collection,typename Filter>
auto operator|(Collection&& collection,Filter filter){
return create_adapter_for_rvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection const& collection,Filter filter){
return create_adapter_for_const_lvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection & collection,Filter filter){
return create_adapter_for_non_const_lvalue_collection(collection,filter);
}
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 have an attribute vector that can hold different types:
class base_attribute_vector; // no template args
template<typename T>
class raw_attribute_vector : public base_attribute_vector;
raw_attribute_vector<int> foo;
raw_attribute_vector<std::string> foo;
Based on run-time input for the type, I would like to create the appropriate data structure. Pseudocode:
std::string type("int");
raw_attribute_vector<type> foo;
Obviously, this fails. An easy, but ugly and unmaintainable workaround is a run-time switch/chained if:
base_attribute_vector *foo;
if(type == "int") foo = new raw_attribute_vector<int>;
else if(type == "string") ...
I read about run-time polymorphism with functors, but found it quite complex for a task that is conceptually easy.
What is the best and cleanest way to make this work? I played around with boost::hana, finding that while I can create a mapping from string to type, the lookup can only be done at compile time:
auto types =
hana::make_map(
hana::make_pair(BOOST_HANA_STRING("int"), hana::type_c<int>),
hana::make_pair(BOOST_HANA_STRING("string"), hana::type_c<std::string>)
);
All possible types are known at compile-time. Any suggestions are highly appreciated. In a perfect solution, I would create the name->type mapping in a single place. Afterwards, I would use it like this
std::vector<base_attribute_vector*> foo;
foo.push_back(magic::make_templated<raw_attribute_vector, "int">);
foo.push_back(magic::make_templated<raw_attribute_vector, "std::string">);
foo[0]->insert(123);
foo[1]->insert("bla");
foo[0]->print();
foo[1]->print();
It is not required for this magic to happen at compile time. My goal is to have as readable code as possible.
I'd use an std::map that has strings as key and std::function as values. I would associate the string with a function that returns your type. Here's an example:
using functionType = std::function<std::unique_ptr<base_attribute_vector>()>;
std::map<std::string, functionType> theMap;
theMap.emplace("int", []{ return new raw_attribute_vector<int>; });
theMap.emplace("float", []{ return new raw_attribute_vector<float>; });
// Using the map
auto base_vec = theMap["int"](); // base_vec is an instance of raw_attribute_vector<int>
Of course, this solution is valid if you only know the string value at runtime.
enum class Type
{
Int,
String,
// ...
Unknown
};
Type TypeFromString(const std::string& s)
{
if (s == "int") { return Type::Int; }
if (s == "string") { return Type::String; }
// ...
return Type::Unknown;
}
template <template <typename> class>
struct base_of;
template <template <typename> class C>
using base_of_t = typename base_of<C>::type;
And then the generic factory
template <template <typename> class C>
std::unique_ptr<base_of_t<C>> make_templated(const std::string& typeStr)
{
Type type = TypeFromString(typeStr);
static const std::map<Type, std::function<std::unique_ptr<base_of_t<C>>()>> factory{
{Type::Int, [] { return std::make_unique<C<int>>(); } },
{Type::String, [] { return std::make_unique<C<std::string>>(); } },
// ...
{Type::Unknown, [] { return nullptr; } }
};
return factory.at(type)();
}
a specialization is needed for each base:
template <>
struct base_of<raw_attribute_vector> {
using type = base_attribute_vector;
};
And then
auto p = make_templated<raw_attribute_vector>(s);
Demo
I'd probably do something like this:
Features:
1 - time registration of objects by passing a named prototype
constant time lookup at runtime
lookup by any type which can be compared to std::string
-
#include <unordered_map>
#include <string>
struct base_attribute_vector { virtual ~base_attribute_vector() = default; };
template<class Type> struct attribute_vector : base_attribute_vector {};
// copyable singleton makes handling a breeze
struct vector_factory
{
using ptr_type = std::unique_ptr<base_attribute_vector>;
template<class T>
vector_factory add(std::string name, T)
{
get_impl()._generators.emplace(std::move(name),
[]() -> ptr_type
{
return std::make_unique< attribute_vector<T> >();
});
return *this;
}
template<class StringLike>
ptr_type create(StringLike&& s) const {
return get_impl()._generators.at(s)();
}
private:
using generator_type = std::function<ptr_type()>;
struct impl
{
std::unordered_map<std::string, generator_type, std::hash<std::string>, std::equal_to<>> _generators;
};
private:
static impl& get_impl() {
static impl _ {};
return _;
}
};
// one-time registration
static const auto factory =
vector_factory()
.add("int", int())
.add("double", double())
.add("string", std::string());
int main()
{
auto v = factory.create("int");
auto is = vector_factory().create("int");
auto strs = vector_factory().create("string");
}
Largely based on Jarod42's answer, this is what I will be using:
class base_attribute_vector {};
template<typename T>
class raw_attribute_vector : public base_attribute_vector {
public:
raw_attribute_vector() {std::cout << typeid(T).name() << std::endl; }
};
template<class base, template <typename> class impl>
base* magic(std::string type) {
if(type == "int") return new impl<int>();
else if(type == "float") return new impl<float>();
}
int main() {
auto x = magic<base_attribute_vector, raw_attribute_vector>("int");
auto y = magic<base_attribute_vector, raw_attribute_vector>("float");
}
Short answer: no, you can't instruct the compiler to evaluate a runtime condition in compile time. Not even with hana.
Long answer: there are some (mostly language independent) patterns for this.
I'm assuming that your base_attribute_vector has some virtual method, most likely pure, commonly called an interface in other languages.
Which means that depending on the complexity of your real problem, you probably want a factory or an abstract factory.
You could create a factory or abstract factory without virtual methods in C++, and you could use hana for that. But the question is: is the added complexity really worth it for that (possibly really minor) performance gain?
(also if you want to eliminate every virtual call, even from base_attribute_vector, you have to make everything using that class a template, after the entry point where the switch happens)
I mean, have you implemented this with virtual methods, and measured that the cost of the virtual calls is too significant?
Edit: another, but different solution could be using a variant type with visitors, like eggs::variant.
With variant, you can create classes with functions for each parameter type, and the apply method will switch which function to run based on it's runtime type.
Something like:
struct handler {
void operator()(TypeA const&) { ... }
void operator()(TypeB const&) { ... }
// ...
};
eggs::variant< ... > v;
eggs::variants::apply(handler{}, v);
You can even use templated operators (possibly with enable_if/sfinae), if they have common parts.