I understand how Polymorphism and Generics interact in other programming languages (Java, C#, Typescript, ect.). In C++ however it feels like a pattern I would like to utilize fails.
In this example I want to have a list of Names which extend Words. I want to pass my list of names into a method which accepts a list of words, but I cannot. I can populate a list of words with my names, this however loses the type information, meaning I cannot call any methods inherit to the Name class.
#include <iostream>
#include <string>
#include <list>
class Word{
public:
virtual void say() = 0;
};
class Name : public Word{
std::string name;
public:
Name(std::string name){
this-> name = name;
}
void say() override{
std::cout << name << std::endl;
}
void important_name_function(){
// Something very important I want to call
}
};
void say_one(Word* w){
w-> say();
}
void say_all(std::list<Word*> list){
for(Word* w: list){
w-> say();
}
}
int main(){
std::list<Word*> words = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
say_one(words.front()); //Works, due to the magic of polymorphism
say_all(words); //Works, due to the magic of polymorphism
std::list<Name*> names = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
say_one(names.front()); //STILL works due to the magic of polymorphism AND type information is retained
say_all(names); //Fails but feels like it shouldn't
}
In, for example, Java I would be able to solve this issue by defining say all as
static <T extends Word> void say_all (java.util.LinkedList<T> list){
for(T w:list){
w.say();
}
}
However, looking for this solution in C++ gets what to my eyes looks like an ugly solution (C++ equivalent of using <T extends Class> for a java parameter/return type)
To me this means that one of the following is true:
This pattern is inherently undesirable, and should not be pursued.
The solution I dismissed as ugly is in fact the best solution and/or
I am incorrectly accessing it as ugly There is another solution to
creating this pattern
I am incorrectly assessing it as ugly
That.
I don't find the following ugly:
template<class T>
void say_all(const std::list<T*>& list) {
for (T* w : list) {
w->say();
}
}
Note that you don't have to restrict T at all in your example. Can't really match that in Java.
Only if you actually need to restrict T to an instance of Word:
template<class T, typename = std::enable_if_t<std::is_base_of<Word, T>::value>>
void say_all(const std::list<T*>& list) {
for (T* w : list) {
w->say();
}
}
Or with concepts:
template<typename T>
concept IsWord = std::is_base_of<Word, T>::value;
template<class T> requires IsWord<T>
void say_all(const std::list<T*>& list) {
for(T* w : list) {
w->say();
}
}
Side notes:
avoid copying objects unnecessarily by passing them by reference.
to reduce memory leaks avoid operator new and use std::list<std::unique_ptr<Word>> and std::make_unique instead.
You should be able to implement a generic function similar to java one using ::std::is_base_of type trait:
template
<
typename x_Word
, typename x_Enabled = ::std::enable_if_t
<
::std::is_base_of_v<Word, x_Word>
>
>
auto
say_all(::std::list<x_Word *> & words) -> void
{
for(auto & p_w: words)
{
p_w->say();
}
return;
}
online compiler
You're not wrong -- this is something C++ isn't great at. It doesn't currently have an equivalent to Java's bounded type parameters, meaning that if you want that specific level of control over what say_all can take, rather than just doing template<typename T> void say_all(list<T> const& l) (or even template<typename T> void say_all(T const& l)) and counting on the internal usage to throw errors, you'll need to do that manually, with enable_if and friends.
This is something that the maybe-upcoming C++ "concepts" feature is intended to address:
template<typename T> requires DerivedFrom<T, Word> void say_all(list<T> const& l) { ...
(Note that syntax and standard library support is still subject to change).
Still, in this case that's just in service of a guaranteed, early, and easy-to-troubleshoot compiler error if you try to pass a list of something else in. Honestly, my approach here would probably be to just document that say_all expects a list of something subclassing Name, and rely on a probable compiler error if that gets violated.
Related
Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.
I love the feature of Interfaces in Java, and was looking forward to the new C++20 standart, introducing concepts.
On a current project i will have multiple implementations for the same thing. The rest of the code should be unaffected by that and handel them all in a general "one fits all" way. Further, to help other people coding there own implementation of this exchangeable part, i would like to have a central place for the documentation, describing all needed parts.
I tried to get this working for some time now, but i keep one struggeling with the C++20 concepts. Since nothing really worked i discribe what i would like to have with a small example:
/* Should have a element type, like float, int, double, std::size_t,... */
template <typename Class>
concept HasElementType = requires {
typename Class::Element;
};
/* Central place for the documentation: in the concept.
* Since all relevant parts should be listed here, they can be documentated.
*/
template < typename Class, typename T>
concept HasFunctions = requires {
Class::Class(int); /* has constructor with int */
T Class::field; /* has field with name "field" of type T */
int Class::foo(T); /* has method foo, taking T, returning int */
T Class::bar(int); /* has method bar, taking int, returning T */
void Class::foobar(); /* has method foobar, taking void, returnung void */
};
/* put both togetter */
template <typename Cls>
concept MyInterface = HasElementType<Cls> && HasFunctions<Cls,typename Cls::Element>;
The above concept MyInterface should than ensure, that calling the function below via my_function<MyObject>() should work properly for different implementations MyObject ∈ {Implementaion1, Implementaion2,...}.
/* Some example function */
template<MyInterface MyObejct>
void my_function(){
using T = MyObejct::Element;
T t = 5;
MyObejct myObject(1);
T field = myObject.field;
int foo = myObject.foo(t);
T bar = myObject.bar(1);
myObject.foobar();
}
I have 3 questions regarding this:
Is it possible with concepts, to accomplish that?
Is this in a somewhat clean look possible? Since it should increase the readability via accessible documentation, it would not be usefull if the code for the concept is barely readable.
Are concepts in generall the right approche, or are there other/better ways to accomplish that?
Thanks, moro
You haved asked multiple things, so I answer one by one. First your "HasElement" concept.
Here you can see how it works:
#include <iostream>
#include <type_traits>
class AWithStaticElement{
public:
static int Element;
};
int AWithStaticElement::Element = 12;
class AWithInstanceElement{
public:
int Element;
};
class AWithElementType{
public:
using Element = int;
};
class AWithoutElement{
};
template<typename T>
requires std::is_member_pointer_v<decltype(&T::Element)>
void Foo(T t)
{
std::cout << "Has instance Element " << t.Element << "\n";
}
template<typename T>
requires std::is_pointer_v<decltype(&T::Element)>
void Foo(T t)
{
std::cout << "Has static Element " << t.Element << "\n";
}
template<typename T>
requires requires (T t) { typename T::Element; }
void Foo(T t)
{
std::cout << "Has Element type\n";
}
template<typename... T>
void Foo(T&&... t)
{
std::cout << "Has no Element!\n";
}
int main()
{
Foo(AWithStaticElement{});
Foo(AWithInstanceElement{});
Foo(AWithElementType{});
Foo(AWithoutElement{});
}
With concepts you can basically give a set of requirements a name. If a concepts is long you don't need to repeat it all the time.
You have a plausible idea, but that's just not the syntax for requires-expressions. You want something like
template < typename Class >
concept HasFunctions = requires(Class c, Class::Element e) {
Class(1); // don't use a null pointer constant here!
{ c.field } -> std::same_as<decltype(e)>;
{ c.foo(e) } -> std::same_as<int>;
{ c.bar(1) } -> std::same_as<decltype(e)>;
c.foobar();
};
Note that there's no need to test Class::Element separately: if that type doesn't exist, then the atomic constraint simply evaluates to false as desired.
This isn't quite as strict as your phrasing suggests; it's sufficient that the class be constructible from an int (possibly via implicit conversions, default arguments, constructor templates, etc.), for example, and it ignores the return type of foobar entirely. However, as is rapidly becoming common advice for constraint authors, why do you care if foobar returns something? If you expect it to be void, you're not going to do much with the return value anyway. It's generally superior to require the interface that you will use (e.g., that you will pass an int here and ignore a value there) rather than trying to describe the implementation of the type in question. Accordingly, you might consider relaxing the std::same_as as well, perhaps with std::convertible_to.
I have a class which acts on a list of elements, like so:
template <typename T>
class MyClass {
...
};
I need to implement certain methods, that work both on a vector<T> and a string - which is what most of the users would be using when they initialise an object as MyClass<char>().
Due to this I am being forced to follow this overloading pattern:
void method(vector<T> elements){
}
void method(string elements){
method(convertStringToVector(elements));
}
where vector<char> convertStringToVector(string str) is defined as expected.
That is, for each class method I am being forced to add an additional method that overloads for string. I find it an unnecessary overhead, and also sometimes forget to add it.
I will be doing exactly the same things on the string as I would do on the corresponding vector - same methods will be called in both cases, same iteration, etc. Thus, I was wondering if there is a cleaner way to do this, without adding much code overhead. Is there?
One possible way is to implement your method as a template, whose parameter is not restricted to be either string or vector<char>.
template <typename T>
struct MyClass
{
template <class C>
void method(C container);
};
This solves your problem because one implementation is enough for both cases:
template <typename T>
template <class C>
void MyClass<T>::method(C container)
{
std::cout << "Container has size " << container.size() << '\n';
std::cout << "First element is " << container[0] << '\n';
}
However, this will work on any container. It's not clear whether this is good (code is generic) or bad (code allows undesirable instantiations).
Imagine what happens when people try to send vector<int> instead of vector<char> to your method by mistake. Because you didn't build your code for this case, it will either display obscure compilation errors, or generate code which silently does the wrong thing at runtime.
You can create a class to use as parameter to your methods, which accepts both std::vector<char> and std::string:
class MyString
{
public:
MyString(const std::vector<char>& v) : vector(v) {}
MyString(std::vector<char>&& v) : vector(std::move(v)) {}
MyString(const std::string& s) : vector(convertStringToVector(s)) {}
std::vector<char> vector;
};
void method(MyString elements)
{
}
You can define convertStringToVector as method of your class like this:
vector<T> convertStringToVector(string str)
or a separate template function, if you need it independently for other classes.
Also I would pass the argument as reference const string &str.
If any template parameter you use is incompatible with the algorithm, the compiler will tell you about.
For those special cases you can still write a template specialization like this:
template<>
vector<SPECIALTYPE> convertStringToVector<SPECIALTYPE>(string str)
{
...
}
and implement different code for such SPECIALTYPE.
I have a class which has a template:
template<class T = int> class slider;
The class has a void Process(void) method, so, I think it should be callable regarless of the type, return value is void and there are no parameters to it.
As for now I have this code to call process each frame in my application:
//class menu:
typedef boost::variant<std::shared_ptr<slider<int>>,std::shared_ptr<slider<float>>,std::shared_ptr<slider<double>>,std::shared_ptr<slider<char>>> slider_type;
std::map<std::string,slider_type> Sliders;
//buttons ... etc ...
void Process()
{
if(!Sliders.empty())
{
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
case 0://slider<int>
{
boost::get<std::shared_ptr<slider<int>>>(i->second)->Process();
break;
}
case 1://slider<float>
{
boost::get<std::shared_ptr<slider<float>>>(i->second)->Process();
break;
}
//.....
}
}
}
}
Is it possible to execute the functions Process() like in the following example?
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
boost::get<???Any???>(i->second)->Process();
}
}
If yes, how?
What would such a function return? You can't change the type of a function at runtime. And the point of a variant is that it's contents are determined at runtime.
The only thing it could return is a boost::any. Which is really just exchanging one kind of unknown for another (an unknown that's a lot harder to deal with when you don't know what it contains, mind you). But if you want to see such a visitor:
struct convert_to_any : public boost::static_visitor<boost::any>
{
template<typename T> boost::any operator() (const T& t) {return t;}
};
Use apply_visitor on that, and you will get an any back. Though I fail to see how that's helpful.
In any case, if you're using get on a variant, you are almost certainly doing the wrong thing. The correct way to access the elements of a variant is with a visitor, not with get.
In your case, the visitor should be simple:
struct ProcessVisitor : public boost::static_visitor<>
{
template<typename T> void operator() (const T& t) const {t->Process();}
};
Just use apply_visitor on that. If the variant contains a type that can be used with operator-> and the return value of that function can have Process called on it, then it will.
(Untested code!)
struct CallProcess : static_visitor<>
{
template <class T>
void operator()(const T &t) const
{
t->Process();
}
};
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
boost::apply_visitor(CallProcess(), i->second);
}
No, not at all. You have to visit and deal with the case of every type. That is much better done with a visitor than your switch hack.
It's not possible because boost::variant has no way to know that all the types in the variant have anything in common. In fact, since the compiler generates a distinct class for each template specialization used, the address of the Process() function that would need to be used is different for each type in the boost::variant. To get around this you could abandon variant and use virtual functions and polymorphic classes sharing a common base class.
Am I required to use a visitor class such as class Visitor : public boost::static_visitor<> with boost::variant?
If not, are there reasons not to use a visitor? Are there reasons to prefer a visitor class?
I ask this question because a visitor class appears a redundant aspect to the use of boost::variant.
You are not forced to use a visitor, you can perfectly query for the underlying type using get<T>().
This leads to such code:
int foo(boost::variant<int, std::string, Bar> const& v) {
if (int const* i = get<int>(&v)) {
return *i;
}
if (std::string const* s = get<std::string>(&v)) {
return boost::lexical_cast<int>(*s);
}
if (Bar const* b = get<Bar>(&v)) {
return b->toInt();
}
std::abort(); // ?
}
Which is, arguably, ugly... and furthermore has the issue that should you add one type to the variant suddenly you need to inspect every single use of it in the code to check you are not missing a if somewhere.
On the other hand, should you be using a variant, if you ever fail to handle a case (type) you will be notified with a compile-time error.
In my eyes, using boost::static_visitor is infinitely superior... though I have used the get<T>() alternative a couple times; generally when I only need to check one (or two) types and do not care (at all) about all the others. An alternative would be using a visitor with a template <typename T> void operator()(T const&) const; overload, which is not necessarily cleaner.
If want to have some operation on variant, for example some check, than you may want to have it as visitor.
struct to_str : boost::static_visitor<std::string>
{
template<class T>
std::string operator()(T const & x) const
{
return boost::lexical_cast<std::string>(x);
}
};
On the other hand if you want, for example check if it int and do something with it, you would probably use boost::get e.g.
if(const int * my_int = boost::get<int>(&my_var)) //no-throw form
{
//do smth with int
}