I need to do some equality checks with different types on a class hierarchy. In pseudo code:
#include <string>
#include <memory>
#include <iostream>
using namespace std;
class ComplexType {};
class Property {};
class IntegerProperty : public Property {
public:
int inner;
};
class StringProperty : public Property {
public:
string inner;
};
class ComplexTypeProperty : public Property {
ComplexType inner;
};
int main() {
shared_ptr<Property> p1 = getSomewhere(); //this is in fact a pointer on IntegerProperty
shared_ptr<Property> p2 = getSomewhere(); // this is in fact a pointer on StringProperty
shared_ptr<Property> p3 = getSomewhere(); // this is in fact a pointer on CompleyTypeProperty
ComplexType c;
cout << ((*p1) == 2);
cout << ((*p2) == "foo");
cout << ((*p3) == c);
}
It it simple to provide a operator== for the derived classes, but I cannot cast before checking, because the type of p1 and p2 is not clear at compile time.
Another way I know is to write the operator== function in the Property base class and throw some exceptions if the type is wrong, but I want, that the Property class can be subclassed later without changing the code for Property and it will work, too.
Templating Property is also not (directly) possible, because e.g. in my code a vector<shared_ptr<Property>> has to exist.
Is there some (generic) way to implement main() to get the equality checks, so that later subclassing of Property without changing the class itself is possible?
Have found some way of solving this. I'm not quite happy with the code. So if anyone has a better solution, please provide it.
#include <string>
#include <memory>
#include <iostream>
using namespace std;
class ComplexType {
public:
bool operator==(const ComplexType& i) {
return true;
}
};
inline ostream& operator<<(ostream& os, const ComplexType& c) {
os << "ComplexType";
return os;
}
class Property {
public:
virtual ~Property() {}
};
template <class T>
class LayerProperty : public Property {
private:
T inner;
public:
LayerProperty(T t) : inner(t) {}
bool operator==(const T i) {
return inner == i;
}
};
int main() {
shared_ptr<Property> p1 = make_shared<LayerProperty<int>>(LayerProperty<int>(5));
shared_ptr<Property> p2 = make_shared<LayerProperty<string>>(LayerProperty<string>("foo"));
shared_ptr<Property> p3 = make_shared<LayerProperty<ComplexType>>(LayerProperty<ComplexType>(ComplexType()));
ComplexType c;
cout << ((*dynamic_pointer_cast<LayerProperty<decltype(2)>>(p1)) == 2) << "\n";
// special case std::string
auto a = "foo";
auto s = "";
if (typeid(a) == typeid(s)) {
cout << ((*dynamic_pointer_cast<LayerProperty<decltype(string(a))>>(p2)) == a) << "\n";
}
cout << ((*dynamic_pointer_cast<LayerProperty<decltype(c)>>(p3)) == c) << "\n";
return 0;
}
Related
I have something like this
class myclass
{};
int main()
{
std::string mystring("myclass");
return 0;
}
And I want to do something like std::shared_ptr<mystring> mysharedptr; that is equal to std::shared_ptr<myclass> mysharedptr;. Do you have an idea of how I should proceed?
I want to be able to do something like this because I have 8 classes and I should create a pointer depending of the name. In order to not have a huge structure with a lot of if/else if, I was wondering if there is a solution.
So to answer my question (this works) :
#include <map>
#include <memory>
#include <string>
class Base
{
public:
using Ptr = std::shared_ptr<Base>;
};
class DerivedA : public Base
{
public:
DerivedA() { std::cout << "DerivedA" << std::endl; }
};
class DerivedB : public Base
{
public:
DerivedB() { std::cout << "DerivedB" << std::endl; }
};
template<typename T>
std::shared_ptr<Base> myCreate()
{
return std::make_shared<T>();
}
int main()
{
static const std::map<std::string, std::shared_ptr<Base> (*)()> myMap
{
{"DerivedA", &myCreate<DerivedA>},
{"DerivedB", &myCreate<DerivedB>}
};
Base::Ptr first, second;
first = myMap.find("DerivedA")->second();
std::cout << first << std::endl;
second = myMap.find("DerivedA")->second();
std::cout << second << std::endl;
return 0;
}
I have an issue similar to this C++ vector of CRTP shared pointers but my problem formulation adds the fact that the return type of a function i want to use for all inheriting classes is templated.
In detail lets assume this :
template <class Derived>
class Base {
Derived Value() const {
return static_cast<Derived>(this->Value());
};
};
class ChildDouble : public Base<ChildDouble> {
public:
ChildDouble(double r) : _value(r){};
double Value() const {
return _value;
};
private:
double _value;
};
class ChildString : public Base<ChildDouble> {
public:
ChildString(string s) : _value(s){};
string Value() const {
return _value;
};
private:
string _value;
};
Goal would be to use it somewhat similar as in the following main
void main() {
std::vector<Base*> vec;
vec.push_back(new ChildDouble(3.0));
vec.push_back(new ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
std::cout << "Entry " << counter << " : " << e->Value()
<< std::endl;
counter++;
}
}
The compiler is obviously not happy with this because Base requires a template argument.
Any Ideas how this could be solved? AM I using CRTP here although i should not be using it?
Virtual methods (which is what you'd normally need to get the above working without CRTP) won't work here because the interface is different for Value() in each derived type. Virtual inheritance depends on the signature being the same for everyone, except in a few special cases like with covariant return types. It also won't work because virtual methods can't be templated.
But, you can use std::variant to dynamically dispatch your incompatible interfaces, because it is based on templates. First, define a convenient alias for your variant:
using Child = std::variant<ChildDouble, ChildString>;
And then to use, dispatch with std::visit and a generic lambda:
std::vector<Child> vec;
vec.push_back(ChildDouble(3.0));
vec.push_back(ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
std::visit([&counter](auto&& v) {
std::cout << "Entry " << counter << " : " << v.Value()
<< std::endl;
}, e);
counter++;
}
Demo: https://godbolt.org/z/bENWYW
It doesn't work because the compiler doesn't know which type you want to put in the vector and you need to specified it. If you try vector<Base<double>*>vec; it will works but you can't use the vector with other types like Base, because, it is other type.
The solution is to use std::variant or std::any in place of template.
Now you have an object variant/any the declare value in base will make your life easier.
Also I suggest you:
not to use variables starting with underline '_' because this syntax is used by many internal function of compiler.
not to use raw pointer. use smart_ptr like share_ptr then you don't need to worry to destroy it with delete.
Below the code with the changes:
#include <memory>
#include <vector>
#include <string>
#include <variant>
#include <iostream>
using namespace std;
struct Base {
Base(variant<double, string> val) : value(val) {}
void Print() { //just to ilustrate how it works. Better use ostream
if (holds_alternative<double>(this->value))
cout << get<double>(this->value);
else if (holds_alternative<string>(this->value))
cout << get<string>(this->value);
}
protected:
variant<double, string> value;
variant<double, string> BaseValue() const { return this->value; };
};
struct ChildDouble : public Base {
ChildDouble(double r) : Base(r) {};
double Value() const { return get<double>(this->BaseValue()); }
};
struct ChildString : public Base {
ChildString(string s) : Base(s) {};
string Value() const { return get<string>(this->BaseValue()); };
};
int main() { //must return int not void
vector<shared_ptr<Base>>vec;
vec.emplace_back(new ChildDouble(3.0));
vec.emplace_back(new ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
cout << "Entry " << counter << " : "; e->Print(); cout << endl;
++counter;
}
}
I have a templated class Parameter which can (or must) be specialized.
I want to put all my parameters in a container.
How to do this if my parameters are instanciated with different types?
In the class Container, I would like to have a vector<Parameter*> from different types (int, double, ...) or something equivalent which seems to not possible.
If the Parameter class is derived from a base class, then The Container can declare the vect as vector<Base*>. But in this case, we can do nothing specific in Container::foo.
Below is my source example. One of my parameters is a QString which is not compatible with ostream.
Thanks for your comments.
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
/*
class Base {
};
*/
template<typename T> class Parameter /*: public Base */ {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(Parameter *base) {
vect.push_back(base);
}
void foo() {
/* do something with the parameters */
}
private:
vector<Parameter*> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
}
Many thanks to you comments. I will follow your advice and use boost::any.
Here is the updated version :
#include <boost/any.hpp>
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
template<typename T> class Parameter {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(boost::any base) {
vect.push_back(base);
}
void foo() {
cout << "do something with the parameters\n";
for (vector<boost::any>::iterator i = vect.begin(); i != vect.end(); ++i) {
boost::any a = (*i);
if (a.type() == typeid(Parameter<int>*)) {
Parameter<int> *ai = boost::any_cast<Parameter<int> *>(a);
cout << ai->getFoo() << endl;
} else if (a.type() == typeid(Parameter<QString>*)) {
Parameter<QString> *aq = boost::any_cast<Parameter<QString> *>(a);
cout << aq->getFoo() << endl;
} else {
cout << "unknown type:" << a.type().name() << endl;
}
}
}
private:
vector<boost::any> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
container.foo();
}
The correct solution is to write good enough interface for the Base class so that you can do everything you need to do:
class Base {
public:
virtual void *GetVal() const=0;
virtual void SetVal(void *ptr)=0;
virtual std::string Type() const=0;
virtual std::string GetAsString() const=0;
};
While this might not be what you want, it still allows passing values from one parameter to the next. Once you want the actual value, you do need to know the type on compile-time. Switch-case for the type might help with making it runtime.
You could use Boost.Any which can hold any type of data. You would then use boost::any_cast<> to convert the object back to the correct type.
Other than that, you'll have to go for the base class approach, but as you mentioned, it could be hard to then make Container::foo do anything useful.
One way you could solve this problem is to have all your foo functions take a string as a parameter, then each specific implementation of the function would parse that string and convert it to the correct type.
Edit: Boost.Any example:
#include <iostream>
#include <boost/any.hpp>
int main()
{
boost::any param = 89;
// This will fail because `param` is currently holding an int
// not a char
char ch = boost::any_cast<char>(param);
// This works
int i = boost::any_cast<int>(param);
// You can always change the value and type of what
// `param` is holding
param = "example";
}
Every thing inside a container has to be the same type. I have done something similar to your approach where I made a base class that had some useful generic interface and the derived class was templated. The only other way to approach a solution would involve defining a base class function to return a value to indicate the type and then downcasting the base.
I have set of classes. Each class is inherited from another class. The relationship is as follows. (I am just posting how one class inherited from another class, just to get idea for all of you)
class LineNumberList : public MyVector <LineNumber > //top class
class MyVector : public std::vector <Type>
class LineNumber : public ElementNumber
class ElementNumber { //this is the base class
protected:
int number;
public:
ElementNumber(int p){number=p;}
// some more codes //
}
Now, I want to implement a function which can be used to find elements inside my topclass i.e. LineNumberList. I tried with standard find function, but it doesn’t work. Can anyone help me to implement similar find function for my case, it is highly appreciated.
"I tried with standard find function, but it doesn’t work." Works for me:
#include <vector>
#include <iostream>
#include <algorithm>
class ElementNumber {
protected:
int number;
public:
ElementNumber(int p) :number(p) {}
bool operator==(const ElementNumber&e) { return number == e.number; }
};
class LineNumber : public ElementNumber {
public:
LineNumber(int p) : ElementNumber(p) {}
};
template <class Type>
class MyVector : public std::vector<Type> {
};
class LineNumberList : public MyVector<LineNumber> {
};
// EDIT: add local implementation of std::find
template<class InputIterator, class T>
InputIterator myfind ( InputIterator first, InputIterator last, const T& value )
{
http://www.cplusplus.com/reference/algorithm/find/
for ( ;first!=last; first++) if ( *first==value ) break;
return first;
}
int main() {
LineNumberList ll;
LineNumber l(7);
ll.push_back(l);
std::cout << std::boolalpha << !(std::find(ll.begin(), ll.end(), l) == ll.end()) << "\n";
std::cout << std::boolalpha << !(::myfind(ll.begin(), ll.end(), l) == ll.end()) << "\n";
}
One can use the STL binary search algorithms (binary_search, upper_bound, lower_bound) to search a vector of Base pointers for a derived object, as shown below. Since Base is abstract (protected constructor), one has to instantiate a Derived object for the search functions, which is slightly ugly.
I want to search the vector for the first Derived above a given time. Can I do this without arbitrarily picking and instantiating one of my many inherited classes?
#include <algorithm>
#include <vector>
#include <stdio.h>
using namespace std;
class Base {
protected:
Base(double t, int d) : data(d), time(t) {}
public:
double time;
int data;
virtual void print() {
printf("Base: data = %d, time = %.1f\n",data,time);
}
};
class Derived : public Base {
public:
Derived(double t, int d) : Base(t,d) {}
virtual void print() {
printf("Derived: data=%d, time=%.1f\n",data,time);
}
};
struct BaseTimeComp {
bool operator()(Base* a, Base* b) { return a->time < b->time; }
};
int main()
{
vector<Base*> v;
for(int i=0; i<5; i++) { v.push_back(new Derived(i+0.4,i)); }
Base* pLow = *(lower_bound(v.begin(),v.end(),
new Derived(3.5,0), //NOT "new Base(3.5,0)"
BaseTimeComp()));
printf("lower bound for time=3.5:\n");
pLow->print();
}
The program prints:
lower bound for time=3.5:
Derived: data=4, time=4.4
The target of the comparison doesn't have to be the same type as the contents of the container, it just has to be something you can compare to the container:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
int i = *(lower_bound(v.begin(), v.end(), 1.5)); // <<< NOTE: floating point "value"
cout << i << endl;
}
Your assumption that you have to make some kind of Base is wrong. You can define a BaseKey which is suitable for your comparisons as long as your explicit (or implied) comparison operator knows what to do.
The comment below is also wrong, as this more complex example demonstrates:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct A {
int x;
A(int _x) :x(_x) { }
bool operator < (double d) { return x < d; }
};
int main()
{
vector<A> v;
v.push_back(A(1));
v.push_back(A(2));
v.push_back(A(3));
int i = (lower_bound(v.begin(), v.end(), 1.5))->x;
cout << i << endl;
}
You can also use a comparision type explicitly (which helps with order of operations problems such as you might find with upper_bound):
class CompareADouble {
public:
bool operator () (const double d, A& a) { return d < a.x; }
};
int main()
{
vector<A> v;
v.push_back(A(1));
v.push_back(A(2));
v.push_back(A(3));
int i = (upper_bound(v.begin(), v.end(), 1.5, CompareADouble()))->x;
cout << i << endl;
}
A binary_search example providing both comparisons with polymorphism:
class CompareADouble {
public:
bool operator () (const double d, A& a) { return d < a.x; }
bool operator () (A& a, const double d) { return a.x < d; }
};
...
bool exists = binary_search(v.begin(), v.end(), 1.5, CompareADouble());
cout << exists << endl; // false
exists = binary_search(v.begin(), v.end(), 1.0, CompareADouble());
cout << exists << endl; // true because 1.0 < 1 == false && 1 < 1.0 == false
You could pass a null pointer, and design your comparison function ignore it, and only test the other object for a specific attribute.
You could, in a way, by using a static method:
class Base {
...
public:
static Base *newSearchInstance(double t, int d) {return new Base(t,d);};
...
};
and in the call to LowerBound:
Base* pLow = *(lower_bound(v.begin(),v.end(),
Base::newSearchInstance(3.5,0), //<------
BaseTimeComp()));
This means you don't have to have knowledge of any of the derived classes, but getting an instance of Base kind of defeats the purpose of Base being abstract in the first place. You would just as well make the constructor public.