I have a templated C++ class that exposes a number of methods, e.g
template<int X, int Y>
class MyBuffer {
public:
MyBuffer<X,Y> method1();
};
Now, I want to expose additional methods to this class if X == Y. I have done this by subclassing MyBuffer,
template<int X>
class MyRegularBuffer : public MyBuffer<X,X> {
public:
MyRegularBuffer method2();
};
Now, the problem is that I want to be able to do e.g.
MyRegularBuffer<2> buf = ...
MyRegularBuffer<2> otherBuf = buf.method1().method2();
But I am not sure how to accomplish this. I tried to think of copy constructors, conversion operators, etc, but my C++ skills are unfortunately a bit rusty.
EDIT: I should add that creation of these objects is relatively cheap (and also, it won't happen a lot), which means it would be OK to do something like this:
MyRegularBuffer<2> buf = ...
MyRegularBuffer<2> temp = buf.method1(); // Implicit conversion
MyRegularBuffer<2> otherBuf = temp.method2();
The question is then, how can I define the conversion like that. The conversion operator needs to be in MyBuffer, I think, but I want it to be available only if X==Y.
You don't need a separate class to represent the special behaviour. Partial specialization allows you to treat some of the MyBuffer <X,Y> cases specially and give them extra methods.
Keep your original declaration of MyBuffer<X,Y> and add this:
template<int Y>
class MyBuffer<Y, Y> {
public:
MyBuffer<Y,Y> method1();
MyBuffer<Y,Y> method2();
};
MyBuffer<1,2> m12; m12.method2(); // compile fail, as desired, as it doesn't have such a method because 1 != 2
MyBuffer<2,2> m22; m22.method2(); // compile success
Edit: my final lines weren't very useful after all, as pointed out by Georg in the comments, so I've deleted them.
I'd go for CRTP here:
template<int X, int Y, class Derived>
struct MyBufferBase {
// common interface:
Derived& method1() { return *static_cast<Derived*>(this); }
};
template<int X, int Y>
struct MyBuffer : MyBufferBase<X, Y, MyBuffer<X,Y> > {
// basic version
};
template<int X>
struct MyRegularBuffer : MyBufferBase<X, X, MyRegularBuffer<X> > {
// extended interface:
MyRegularBuffer& method2() { return *this; }
};
It's possible to do what you want if method1 and method2 return a reference to *this. Otherwise, you're going to need to either do a conversion, or make method1 virtual.
The trick is to have a MyRegularBuffer::method1 that calls MyBuffer::method1, then a way to convert the resultant MyBuffer<X,X> into a MyRegularBuffer<X>:
template<int X>
class MyRegularBuffer : public MyBuffer<X,X>
{
public:
MyRegularBuffer<X>()
{}
MyRegularBuffer<X>(MyBuffer<X,X>)
{
// copy fields, or whatever
}
MyRegularBuffer<X> method2();
MyRegularBuffer<X> method1()
{
MyRegularBuffer<X> ret(MyBuffer<X,X>::method1());
return(ret);
}
};
Related
Consider the situation of having some class A together with a factory class Factory. A is supposed to have only private constructors (to prevent the user from creating such an object), and is friends with Factory, who can then construct Objects of type A.
A also has some private attribute which cannot be set in in A's constructor (because the value is not known yet), but will be computed by Factory and then set correctly before A is returned.
Now i have the situation that I have similar classes B,C, ... to A that should also be constructed by Factory, which will thus be a template.
So the situation is currently
template<typename T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
friend class Factory<A>;
private:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
friend class Factory<B>;
private:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void foo() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
Actually, an instance of Factory<A> also stores some values that are used in the construction of some A, that are set by the constructor of Factory, this is why i use instances of the Factory and not static methods.
Now comes the point: I would like to use Concepts in order to constrain the template<typename T> that is used by the Factory, so something like:
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<Factorable t>
class Factory { //...
As expected, this does not work, because the corresponding required expressions are private and thus not available, Factorable<A> will just be false.
Now, hanging the attribute and constructor of class A to public will thus lead to a nice generic Factory and the below (correct) code
template<Factorable T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<Factorable T>
friend class Factory;
public:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
template<Factorable T>
friend class Factory;
public:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void use() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
Note the subtlety that we now had to declare all generic variants of Factory as a friend of classes A and B, since Factory<A> cannot be evaluated while defining class A itself and thus not explicitly declared as a friend. I am however fine with this, since in the implementation of the Factory<T> we will not access other classes than T anyways (It would however be nice to achieve a friend class Factory<A>, but this is not my priority).
But this approach leads to the massive problem that a user can now generate instances of A by just calling its constructor, which just sets _value = nullptr, so we get 'invalid' instances of A that may lead to undefined behaviour etc. So this is really not the way to go for, although the use of the concepts in above expression would be nice.
So my question now is:
How can I achieve both of the above, in order have private constructors and attributes, but still use concepts
I found out that concepts are actually context-sensitive, in the sense that the required expressions are checked in the invoking context, so in fact the following code will correctly compile
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<typename T>
class Factory {
static_assert(Factorable<T>);
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<typename T>
friend class Factory;
private:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
template<typename T>
friend class Factory;
private:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void use() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
since now the compiler will first accept any typename T as generic and then check the concept for the given T within the context of the class, which will thus evaluate to true, since Factory is friends with both A and thus has access to its data members.
So this approach can actually guarantee user-safe instantiation of class A and throwing corresponding errors when the static_assertion fails to evaluate, but of course this is not the way a concept is intended to use, since then we basically just fall back to duck-typing as it was before the introduction of concepts and manually assert certain things, which loses the desired properties of concepts (late error throwing etc., but also losing IDE completion).
A solution would of course be to have the possibility to require the presence of private attributes in a concept, something like the following
//Suppose the existence of a function like std::ignore_private(expression)
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(std::ignore_private(t._value))>;
}
where std::ignore_private is of course completely made up (and i also don't know what syntax it actually should have), but you get the point what i actually want.
So, is there any way to express something like this in a concept?
At least, i could imagine that, since checking for private attributes is a constexpr and can also be checked at compile time.
If you don't find a better solution a compromise could be to constrain the method:
template<typename T>
class Factory {
public:
T get() requires Factorable<T>
{
// ...
}
};
After experimenting around a while, i came up with the following workaround, using an additional Tester class, that has to be declared friend by A:
template<typename T>
concept PrivateFactorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<typename T> class Tester {
public:
constexpr static bool factorable = PrivateFactorable<T>;
};
template<typename T>
concept Factorable = Tester<T>::factorable;
template<Factorable T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a meaningful value here
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<Factorable T>
friend class Factory;
friend class Tester<A>;
public:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
This will correctly redirect Factorable<A> into the tester class, who can evaluate the concept withing friend context with A. The only downside is that A has to have yet another friend (and we get some more concepts in the background), but this seems okay
First, I think it’s a compiler bug if the value of a concept depends on the lexical location of requesting it.
It could interfere with subsumption where that matters, but you can write the constraint directly:
template<class T> requires std::same_as<void*, decltype(T::_value)>
class Factory {…};
Substitution failures in a constraint cause it to be unsatisfied whether it’s in a concept or not.
My problem with Qt & pimpl is not actually a problem, more a request for best-practice advice.
So: we've got quite a large project with lots of GUI and other Qt classes. Readability of headers is required for fine collaboration, reducing compilation time is also a matter of regular consideration.
Thus, there I have lots of classes like:
class SomeAwesomeClass: public QWidget
{
Q_OBJECT
public:
/**/
//interface goes here
void doSomething();
...
private:
struct SomeAwesomeClassImpl;
QScopedPointer<SomeAwesomeClassImpl> impl;
}
Of course, the Pimpl class is in the .cpp file, works fine, like:
struct MonitorForm::MonitorFormImpl
{
//lots of stuff
}
This piece of software is supposed to be crossplatform (not a surprise) and cross-compiled without significant effort. I know about Q_DECLARE_PRIVATE, Q_D and other macros, they make me think more about Qt MOC, possible differences in Qt versions (because of legacy code), but this way or another there are many lines of code contatinig something like
impl->ui->component->doStuff();
//and
impl->mSomePrivateThing->doOtherStuff()
//and even
impl->ui->component->SetSomething(impl->mSomePrivateThing->getValue());
The pseudo-code above is a much simplified version of the real one, but most of us are fine with it. But some colleagues insist, that it's rather bothering to write and read all those long lines, especially when impl->ui->mSomething-> is repeating too often. The opinion states, that Qt marcos also add visual garbaging to the situation in the end. Seversl #define's could help, but those are considered generally bad practice.
In short, based on your experience, is there a way to make pimpl usage more laconic? Maybe it isn't truly required as often as seems, in non-library classes for example? Maybe the goals of it's usage are not equal, depending on circumstances?
What's the proper way to cook it, anyway?
Introduction
I know about Q_DECLARE_PRIVATE, Q_D and other macros
You know about them, but have you actually used them and understand their purpose, and - for the most part - their inevitability? Those macros weren't added to make stuff verbose. They are there because you end up needing them.
There are no differences in Qt PIMPL implementation between Qt versions, but you are depending on Qt's implementation details when you inherit from QClassPrivate, should you do so. The PIMPL macros have nothing to do with moc. You can use them in plain C++ code that doesn't use any Qt classes at all.
Alas, there's no escaping what you want for as long as you implement the PIMPLs the usual way (which is also Qt way).
Pimpl-pointer vs this
First of all, let's observe that impl stands for this, but the language lets you skip using this-> in most cases. Thus, it's nothing too foreign.
class MyClassNoPimpl {
int foo;
public:
void setFoo(int s) { this->foo = s; }
};
class MyClass {
struct MyClassPrivate;
QScopedPointer<MyClassPrivate> const d;
public:
void setFoo(int s);
...
virtual ~MyClass();
};
void MyClass::setFoo(int s) { d->foo = s; }
Inheritance demands...
Things become generally outlandish when you have inheritance, though:
class MyDerived : public MyClass {
class MyDerivedPrivate;
QScopedPointer<MyDerivedPrivate> const d;
public:
void SetBar(int s);
};
void MyDerived::setFooBar(int f, int b) {
MyClass::d->foo = f;
d->bar = b;
}
You'll want to re-use a single d-pointer in the base class, but it will have the wrong type in all derived classes. Thus you might think of casting it - that's even more boilerplate! Instead, you use a private function that returns a correctly-cast d-pointer. Now you need to derive both public and private classes, and you need private headers for the private classes, so that the derived classes can use them. Oh, and you need to pass the pointer to the derived pimpl to the base class - because that's the only way you can initialize the d_ptr while keeping it const, as it must be. See - Qt's PIMPL implementation is verbose because you do actually need all of it to write safe, composable, maintainable code. No way around it.
MyClass1.h
class MyClass1 {
protected:
struct Private;
QScopedPointer<Private> const d_ptr;
MyClass1(Private &); // a signature that won't clash with anything else
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() const { return (const Private*)d_ptr; }
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1::Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(Private &p) : d_ptr(&p) {}
MyClass1::MyClass1() : d_ptr(new Private) {}
MyClass1::~MyClass1() {} // compiler-generated
void MyClass1::setFoo(int f) {
d()->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2 : public MyClass1 {
protected:
struct Private;
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() { return (const Private*)d_ptr; }
public:
MyClass2();
~MyClass2() override; // Override ensures that the base had a virtual destructor.
// The virtual keyword is not used per DRY: override implies it.
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2::Private : MyClass1::Private {
int bar;
};
MyClass2.cpp
MyClass2::MyClass2() : MyClass1(*new Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
d()->foo = f;
d()->bar = b;
}
Inheritance, Qt way
Qt's PIMPL macros take care of implementing d() functions. Well, they implement d_func() and then you use the Q_D macro to obtain a local variable that is simply d. Rewriting the above:
MyClass1.h
class MyClass1Private;
class MyClass1 {
Q_DECLARE_PRIVATE(MyClass1)
protected:
QScopedPointer<Private> d_ptr;
MyClass1(MyClass1Private &);
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(MyClass1Private &d) : d_ptr(*d) {}
MyClass1::MyClass1() : d_ptr(new MyClass1Private) {}
MyClass1::MyClass1() {}
void MyClass1::setFoo(int f) {
Q_D(MyClass1);
d->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2Private;
class MyClass2 : public MyClass1 {
Q_DECLARE_PRIVATE(MyClass2)
public:
MyClass2();
~MyClass2() override;
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2Private : MyClass1Private {
int bar;
};
MyClass2.cpp
MyClass2() : MyClass1(*new MyClass2Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
Q_D(MyClass2);
d->foo = f;
d->bar = b;
}
Factories simplify pimpl
For class hierarchies that are sealed (i.e. where the user doesn't derive), the interface can be sanitized from any private details whatsoever by the use of factories:
Interfaces
class MyClass1 {
public:
static MyClass1 *make();
virtual ~MyClass1() {}
void setFoo(int);
};
class MyClass2 : public MyClass1 {
public:
static MyClass2 *make();
void setFooBar(int, int);
};
class MyClass3 : public MyClass2 {
public:
static MyClass3 *make();
void setFooBarBaz(int, int, int);
};
Implementations
template <class R, class C1, class C2, class ...Args, class ...Args2>
R impl(C1 *c, R (C2::*m)(Args...args), Args2 &&...args) {
return (*static_cast<C2*>(c).*m)(std::forward<Args2>(args)...);
}
struct MyClass1Impl {
int foo;
};
struct MyClass2Impl : MyClass1Impl {
int bar;
};
struct MyClass3Impl : MyClass2Impl {
int baz;
};
struct MyClass1X : MyClass1, MyClass1Impl {
void setFoo(int f) { foo = f; }
};
struct MyClass2X : MyClass2, MyClass2Impl {
void setFooBar(int f, int b) { foo = f; bar = b; }
};
struct MyClass3X : MyClass3, MyClass3Impl {
void setFooBarBaz(int f, int b, int z) { foo = f; bar = b; baz = z;}
};
MyClass1 *MyClass1::make() { return new MyClass1X; }
MyClass2 *MyClass2::make() { return new MyClass2X; }
MyClass3 *MyClass3::make() { return new MyClass3X; }
void MyClass1::setFoo(int f) { impl(this, &MyClass1X::setFoo, f); }
void MyClass2::setFooBar(int f, int b) { impl(this, &MyClass2X::setFooBar, f, b); }
void MyClass3::setFooBarBaz(int f, int b, int z) { impl(this, &MyClass3X::setFooBarBaz, f, b, z); }
This is very basic sketch that should be further refined.
#KubaOber gave an excellent coverage of how pimpl works and how to implement it. One thing not covered that you discussed are the inevitable macros to simplify the boilerplate. Let's take a look at a possible implementation, borrowed from my own Swiss Army knife library, which is clearly based on Qt's take.
Firstly, we need a base public interface and a base private implementation with the boilerplate. Inheriting directly from Qt's implementation is useless if we aren't using Qt (and an incredibly bad idea besides), so we'll just create a lightweight base class for the implementation (or d_ptr) and the implementation's back-pointer to the interface (the q_ptr).
#include <QScopedPointer> //this could just as easily be std::unique_ptr
class PublicBase; //note the forward declaration
class PrivateBase
{
public:
//Constructs a new `PrivateBase` instance with qq as the back-pointer.
explicit PrivateBase(PublicBase *qq);
//We declare deleted all other constructors
PrivateBase(const PrivateBase &) = delete;
PrivateBase(PrivateBase &&) = delete;
PrivateBase() = delete;
//! Virtual destructor to prevent slicing.
virtual ~PrivateBase() {}
//...And delete assignment operators, too
void operator =(const PrivateBase &) = delete;
void operator =(PrivateBase &&) = delete;
protected:
PublicBase *qe_ptr;
};
class PublicBase
{
public:
//! The only functional constructor. Note that this takes a reference, i.e. it cannot be null.
explicit PublicBase(PrivateBase &dd);
protected:
QScopedPointer<PrivateBase> qed_ptr;
};
//...elsewhere
PrivateBase::PrivateBase(PublicBase *qq)
: qe_ptr(qq)
{
}
PublicBase::PublicBase(PrivateBase &dd)
: qed_ptr(&dd) //note that we take the address here to convert to a pointer
{
}
Now to the macros.
/* Use this as you would the Q_DECLARE_PUBLIC macro. */
#define QE_DECLARE_PUBLIC(Classname) \
inline Classname *qe_q_func() noexcept { return static_cast<Classname *>(qe_ptr); } \
inline const Classname* qe_cq_func() const noexcept { return static_cast<const Classname *>(qe_ptr); } \
friend class Classname;
/* Use this as you would the Q_DECLARE_PRIVATE macro. */
#define QE_DECLARE_PRIVATE(Classname) \
inline Classname##Private* qe_d_func() noexcept { return reinterpret_cast<Classname##Private *>(qed_ptr.data()); } \
inline const Classname##Private* qe_cd_func() const noexcept { return reinterpret_cast<const Classname##Private *>(qed_ptr.data()); } \
friend class Classname##Private;
These are fairly self-explanatory: they cast the stored pointer to the appropriate derived type. The macro leverages the class name + "Private" to cast to the right type. This means your private class MUST follow the naming pattern: InterfaceClass becomes InterfaceClassPrivate. For scope resolution to work, they need to be in the same namespace, too. Your private class can't be a member of your public class.
And finally, the accessors, with a C++11 twist:
#define QE_DPTR auto d = qe_d_func()
#define QE_CONST_DPTR auto d = qe_cd_func()
#define QE_QPTR auto q = qe_q_func()
#define QE_CONST_QPTR auto q = qe_cq_func()
Not having to explicitly specify the class name makes usage incredibly easy and less rigid. Should this class be renamed or the function moved to another level in the inheritance hierarchy, you don't have to change the QE_CONST_DPTR statement.
SomeInterface::getFoo() const noexcept
{
QE_CONST_DPTR;
return d->foo;
}
would become:
SomeInterfaceInheritingFromSomeOtherInterface::getFoo() const noexcept
{
QE_CONST_DPTR;
return d->foo;
}
One purpose of PIMPL is to decouple interface from private implementation. Examples like impl->ui->component->doStuff(); are a sign that there is a problem with the scope of the interface. IMHO you should normally not see more than one deep calls.
I.e.
impl->doStuff(); OK
impl->ui->doStuff(); Hmmm, better avoid that.
impl->ui->component->... Uh oh, things go wrong here. Caller needs to know far too much details of the implementation. That's not the purpose of PIMPL!
You may want to read https://herbsutter.com/gotw/_100/, especially the section What parts of the class should go into the impl object?
Assume I have the following 2 classes Super and Deriv where Deriv is a subclass of Super.
Super and Deriv are both templated classes. I want to create a vector of type Super<?>. Where the ? signifies any type. Currently I have come up with this:
#include <iostream>
#include <vector>
template <typename T>
class Super {
public:
T val;
Super(T val) : val(val) {};
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<Super*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
std::cout << a[0]->val << std::endl;
return 0;
}
Of course this does not work because std::vector<Super*> needs a template type like std::vector<Super<int>*>. However the problem with this is I can only add items to the vector of type Super<int>* and not Super<float>*.
How can I alter this code to allow me to add a Super type or it's derivatives to a vector which has any template type like int, float, short etc?
Not sure if this can solve your problem, but it might give you some ideas. The basic idea here is to make a super class for all kinds of T. For fundamental data types, wrapper classes are needed.
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
class SuperT {
public:
virtual std::string AccessData() = 0;
};
class IntWraper : public SuperT {
public:
IntWraper(int i) : val(i) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
int val;
};
class FloatWraper : public SuperT {
public:
FloatWraper(float f) : val(f) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
float val;
};
class RealSuper {
public:
virtual std::string DoSomething() = 0;
};
template <typename T>
class Super : public RealSuper {
public:
T* wraper_val_;
Super(T* w_val) : wraper_val_(w_val) { };
~Super() { if(wraper_val_) delete wraper_val_; };
virtual std::string DoSomething() { return wraper_val_->AccessData(); }
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T* w_val) : Super<T>(w_val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<IntWraper>(new IntWraper(1)));
a.push_back(new Deriv<FloatWraper>(new FloatWraper(1.0f)));
std::cout << a[0]->DoSomething() << std::endl;
std::cout << a[1]->DoSomething() << std::endl;
return 0;
}
This
std::vector<Super*> a;
Is wrong.
There is no type named Super in your code. Yes, you declared something named "Super", but it's not a class, it's a class template.
The name of the feature says it all. It's a template. The compiler will use Super to generate new types at compile time.
For example, Super<int> Refer to the class generated by the compiler when filling the hole T in the Super template.
So why a[0]->val cannot possibly work? Well, imagine this:
template<>
struct Super<std::string> {
std::string my_val;
};
We specialize Super so when instantiated with std::string, it no longer have the val member, but the my_val member.
Now, what do you expect this code to do?
std::vector<Super*> a;
a.push_back(new Deriv<std::string>);
std::cout << a[0]->val << std::endl;
Quite puzzling isn't it? You'd need a compilation error at runtime. Since the existence (or non existence) of variable is determined at compile time, it's not possible.
Now how can we solve your problem?
In your case, it would be as simple as adding an interface above Super, and exposing functions that implements behaviors needed to do your calculations:
struct Interface {
void print(std::ostream) const = 0;
bool lessThan(double) const = 0;
};
template <typename T>
struct Super : Interface {
T val;
Super(T val_) : val{val_} {};
// We implement the needed behavior.
void print(std::ostream o) const override {
o << val << std::endl;
}
// Example of calculation
bool lessThan(double rhs) const override {
return val < rhs;
}
};
Now you can do:
std::vector<Interface*> a;
// ...
a[0]->print(std::cout);
a[0]->lessThan(3.7);
Create a new class RealSuper that cache the type is a possible workaround.
It is not perfect, but I am afraid that it can't be much better than this :-
class RealSuper{ //<-- new class
public: enum TYPE{ type_int, type_float, type_notIni }; // (yes, it is awkward)
TYPE typee = type_notIni;
};
template <typename T> class Super : public RealSuper { //<-- modify
public: T val;
Super(T val) : val(val) {
if( std::is_same<T, int>::value ){
typee = type_int;
}else if( std::is_same<T, float>::value ){
typee = type_float;
}
};
};
template <typename T> class Deriv : public Super<T> {
public: Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
for(auto ele: a){
switch( ele->typee ){
case RealSuper::TYPE::type_int: {
int value=static_cast<Super<int>*>(ele)->val;
std::cout << value << std::endl;
};break;
case RealSuper::TYPE::type_float :{
float value=static_cast<Super<float>*>(ele)->val;
std::cout << value << std::endl;
};break;
}
}
return 0;
}
live demo
Here is another demo to show an approach using the virtual function.
C++ does not have the feature you are asking for (I believe it is a kind of reification). This means you cannot store arbitrary types in location one, then specify arbitrary operations in a completely unrelated source file in location two, then apply the operations to the data in location three.
There are many problems you can solve that are close to what you are asking for: Restrict what operations you want to do at location two, or restrict what types you store at location one, and the problem can be solved. Alternatively, restrict what type x operation pairs you support at location three.
Note that composition of the restricted set of operations can also work.
In theory you can embed a full compiler or interpreter in a C++ binary, compile code and dynamically load it. (This is basically how C#/Java manage reification). This is rather impractical for most problems. The language provides no support for this, but this is C++, you can do anything (write a Java/C# compiler even).
With no information about the underlying problem you need to solve, I cannot tell you which of the above is the correct approach.
This is the reason why "if I had X I coukd solve Y, but I cannot figure out X. I know, I'll just ask stack overflow how to do X!" is known as an X/Y problem. We can probably solve Y, but you asked about X which woukd solve Y without even describing Y. Feel free to post the Y problem and ask about it. Use the [ask a question] button above.
Restrict what types you handle at storage:
Store a std::variant. Use std::visit. Or write your own or use boost::variant.
Restrict what operations to perform:
Use type erasure to generate the per-type operation when you store the type.
Restrict the operation-type pairs at point of call:
Use RTTI to exctract what type is stored, have a large switch switch that then uses solution one.
I would like to know how would you address such a problem:
I have a class Foo:
class Foo
{
public:
Foo() { }
~Foo() { }
float member1() { return _member1; }
private:
float _member1;
// other members etc...
}
A container class that, among other things, holds a container of pointers to Foo instances
class FooContainer
{
public:
FooContainer() { }
~FooContainer() { }
void addFoo(Foo* f) {_foos.push_back(f);}
private:
boost::ptr_vector<Foo> _foos;
}
My problem is this: at runtime I am required to "add" new (completely different) members to Foo, depending on the instructions from the GUI. I could address the problem by creating two "decorators" like this:
class Decorator1
{
public:
int alpha() { return _alpha; }
float beta() { return _beta; }
private:
int _alpha;
float _beta;
}
class Decorator2
{
typedef std::complex<float> cmplx;
public:
cmplx gamma() { return _gamma; }
double delta() { return _delta; }
private:
cmplx _gamma;
double _delta;
}
and then I would create two different Foo implementations:
class Foo1 : public Foo, public Decorator1
{ }
class Foo2 : public Foo, public Decorator2
{ }
and use each one according to the GUI command. However such a change would propagate through all my code and would force me to create two different versions for each class that uses Foo1 and Foo2 (e.g. I'd have to create FooContainer1 and FooContainer2).
A less intrusive way of doing this would be to create
class Bar: public Foo, public Decorator1, public Decorator2
{ }
and use this instead of Foo. In this case I'd call only the functions I need from Decorator1 and Decorator2 and ignore the others, but this seems to go against good OOP techniques.
Any suggestions regarding the problem ?
Why don't you use simple polymorphism like this?
class Foo
{
public:
Foo() { }
virtual ~Foo() { }
float member1() { return _member1; }
private:
float _member1;
// other members etc...
}
class Foo1 : public Foo
{
public:
int alpha() { return _alpha; }
float beta() { return _beta; }
private:
int _alpha;
float _beta;
}
class Foo2 : public Foo
{
typedef std::complex<float> cmplx;
public:
cmplx gamma() { return _gamma; }
double delta() { return _delta; }
private:
cmplx _gamma;
double _delta;
}
class FooContainer
{
public:
FooContainer() { }
~FooContainer() { }
void addFoo(Foo* f) {_foos.push_back(f);}
private:
boost::ptr_vector<Foo> _foos;
}
Then the client code need not change. According to the GUI command you can create Foo1 or Foo2 and add it to the single container. If necessary, you can use the dynamic_cast on Foo pointer to cast to Foo1 or Foo2. But, if you have written the client code properly, then this wouldn't be needed.
It sounds like you're looking to handle mixin-type functionality. To do that, you could use templates. This isn't run time in the sense that copies of each class will be generated, but it does save you the typing.
So for each decorator, do something like:
template<class TBase> class Decorator1 : public TBase
{
public:
void NewMethod();
}
Then you can, for example:
Foo* d = new Decorator1<Foo1>(...);
Of course, the only way to make this work at runtime is to decide which type you're going to create. However, you still end up with the type Foo, Foo1 and Decorator1 so you can cast between them/use RTTI as you need to.
For more on this, see this article and this document
Although I've suggested it as a potential solution, I personally would be tempted to go with the polymorphism suggestion if at all possible - I think that makes for better, easier to maintain code because parts of class implementations aren't scattered all over the place using mixins. Just my two cents - if you think it works, go for it.
the fundamental concept of a class is that it's encapsulated and hence that one cannot add members after the definition (though you can use polymorphism and create derived classes with additional members, but they cannot be called through pointer of the original class: you must cast them to derived which is dangerous), in particular not at run time.
So it seems to me you're requirement breaks the essential idea of OO programming. This suggests a simple solution: use non-member functions. They can be defined at any time, even run time (when you would also need to compile them). The overhead of the function pointer is the same as before (when you would need a pointer to a new member function).
How about policy based templates? Have a template class Foo that takes a class as a template parameter. Then, have two methods that call the decorator methods:
tempate <class Decor>
class Foo
{
public:
Foo() : { __d = Decor() }
~Foo() { }
float member1() { return _member1; }
Decor::method1type decoratorMember1() { return __d.getValueMethod1();}
Decor::method2type decoratorMember2() { return __d.getValueMethod2();}
private:
float _member1;
Decor __d;
// other members etc...
}
Then, in your complex decorator:
class Decor1 {
typedef std::complex<float> method1type;
typedef double method2type;
public:
method1type getValueMethod1() {return _gamma}
method2type getValueMethod2() {return _delta}
private:
method1type _gamma;
method2type _delta;
}
Same for the other. This way, your Foo code can have anything added to it, even if it's already compiled. Just make a declarator class. And instead of instantiating Foo1, do this:
Foo<Decor1> f;
I have code like this:
class RetInterface {...}
class Ret1: public RetInterface {...}
class AInterface
{
public:
virtual boost::shared_ptr<RetInterface> get_r() const = 0;
...
};
class A1: public AInterface
{
public:
boost::shared_ptr<Ret1> get_r() const {...}
...
};
This code does not compile.
In visual studio it raises
C2555: overriding virtual function return type differs and is not
covariant
If I do not use boost::shared_ptr but return raw pointers, the code compiles (I understand this is due to covariant return types in C++). I can see the problem is because boost::shared_ptr of Ret1 is not derived from boost::shared_ptr of RetInterface. But I want to return boost::shared_ptr of Ret1 for use in other classes, else I must cast the returned value after the return.
Am I doing something wrong?
If not, why is the language like this - it should be extensible to handle conversion between smart pointers in this scenario? Is there a desirable workaround?
Firstly, this is indeed how it works in C++: the return type of a virtual function in a derived class must be the same as in the base class. There is the special exception that a function that returns a reference/pointer to some class X can be overridden by a function that returns a reference/pointer to a class that derives from X, but as you note this doesn't allow for smart pointers (such as shared_ptr), just for plain pointers.
If your interface RetInterface is sufficiently comprehensive, then you won't need to know the actual returned type in the calling code. In general it doesn't make sense anyway: the reason get_r is a virtual function in the first place is because you will be calling it through a pointer or reference to the base class AInterface, in which case you can't know what type the derived class would return. If you are calling this with an actual A1 reference, you can just create a separate get_r1 function in A1 that does what you need.
class A1: public AInterface
{
public:
boost::shared_ptr<RetInterface> get_r() const
{
return get_r1();
}
boost::shared_ptr<Ret1> get_r1() const {...}
...
};
Alternatively, you can use the visitor pattern or something like my Dynamic Double Dispatch technique to pass a callback in to the returned object which can then invoke the callback with the correct type.
There is a neat solution posted in this blog post (from Raoul Borges)
An excerpt of the bit prior to adding support for mulitple inheritance and abstract methods is:
template <typename Derived, typename Base>
class clone_inherit<Derived, Base> : public Base
{
public:
std::unique_ptr<Derived> clone() const
{
return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
}
private:
virtual clone_inherit * clone_impl() const override
{
return new Derived(*this);
}
};
class concrete: public clone_inherit<concrete, cloneable>
{
};
int main()
{
std::unique_ptr<concrete> c = std::make_unique<concrete>();
std::unique_ptr<concrete> cc = c->clone();
cloneable * p = c.get();
std::unique_ptr<clonable> pp = p->clone();
}
I would encourage reading the full article. Its simply written and well explained.
You can't change return types (for non-pointer, non-reference return types) when overloading methods in C++. A1::get_r must return a boost::shared_ptr<RetInterface>.
Anthony Williams has a nice comprehensive answer.
What about this solution:
template<typename Derived, typename Base>
class SharedCovariant : public shared_ptr<Base>
{
public:
typedef Base BaseOf;
SharedCovariant(shared_ptr<Base> & container) :
shared_ptr<Base>(container)
{
}
shared_ptr<Derived> operator ->()
{
return boost::dynamic_pointer_cast<Derived>(*this);
}
};
e.g:
struct A {};
struct B : A {};
struct Test
{
shared_ptr<A> get() {return a_; }
shared_ptr<A> a_;
};
typedef SharedCovariant<B,A> SharedBFromA;
struct TestDerived : Test
{
SharedBFromA get() { return a_; }
};
Here is my attempt :
template<class T>
class Child : public T
{
public:
typedef T Parent;
};
template<typename _T>
class has_parent
{
private:
typedef char One;
typedef struct { char array[2]; } Two;
template<typename _C>
static One test(typename _C::Parent *);
template<typename _C>
static Two test(...);
public:
enum { value = (sizeof(test<_T>(nullptr)) == sizeof(One)) };
};
class A
{
public :
virtual void print() = 0;
};
class B : public Child<A>
{
public:
void print() override
{
printf("toto \n");
}
};
template<class T, bool hasParent = has_parent<T>::value>
class ICovariantSharedPtr;
template<class T>
class ICovariantSharedPtr<T, true> : public ICovariantSharedPtr<typename T::Parent>
{
public:
T * get() override = 0;
};
template<class T>
class ICovariantSharedPtr<T, false>
{
public:
virtual T * get() = 0;
};
template<class T>
class CovariantSharedPtr : public ICovariantSharedPtr<T>
{
public:
CovariantSharedPtr(){}
CovariantSharedPtr(std::shared_ptr<T> a_ptr) : m_ptr(std::move(a_ptr)){}
T * get() final
{
return m_ptr.get();
}
private:
std::shared_ptr<T> m_ptr;
};
And a little example :
class UseA
{
public:
virtual ICovariantSharedPtr<A> & GetPtr() = 0;
};
class UseB : public UseA
{
public:
CovariantSharedPtr<B> & GetPtr() final
{
return m_ptrB;
}
private:
CovariantSharedPtr<B> m_ptrB = std::make_shared<B>();
};
int _tmain(int argc, _TCHAR* argv[])
{
UseB b;
UseA & a = b;
a.GetPtr().get()->print();
}
Explanations :
This solution implies meta-progamming and to modify the classes used in covariant smart pointers.
The simple template struct Child is here to bind the type Parent and inheritance. Any class inheriting from Child<T> will inherit from T and define T as Parent. The classes used in covariant smart pointers needs this type to be defined.
The class has_parent is used to detect at compile time if a class defines the type Parent or not. This part is not mine, I used the same code as to detect if a method exists (see here)
As we want covariance with smart pointers, we want our smart pointers to mimic the existing class architecture. It's easier to explain how it works in the example.
When a CovariantSharedPtr<B> is defined, it inherits from ICovariantSharedPtr<B>, which is interpreted as ICovariantSharedPtr<B, has_parent<B>::value>. As B inherits from Child<A>, has_parent<B>::value is true, so ICovariantSharedPtr<B> is ICovariantSharedPtr<B, true> and inherits from ICovariantSharedPtr<B::Parent> which is ICovariantSharedPtr<A>. As A has no Parent defined, has_parent<A>::value is false, ICovariantSharedPtr<A> is ICovariantSharedPtr<A, false> and inherits from nothing.
The main point is as Binherits from A, we have ICovariantSharedPtr<B>inheriting from ICovariantSharedPtr<A>. So any method returning a pointer or a reference on ICovariantSharedPtr<A> can be overloaded by a method returning the same on ICovariantSharedPtr<B>.
Mr Fooz answered part 1 of your question. Part 2, it works this way because the compiler doesn't know if it will be calling AInterface::get_r or A1::get_r at compile time - it needs to know what return value it's going to get, so it insists on both methods returning the same type. This is part of the C++ specification.
For the workaround, if A1::get_r returns a pointer to RetInterface, the virtual methods in RetInterface will still work as expected, and the proper object will be deleted when the pointer is destroyed. There's no need for different return types.
maybe you could use an out parameter to get around "covariance with returned boost shared_ptrs.
void get_r_to(boost::shared_ptr<RetInterface>& ) ...
since I suspect a caller can drop in a more refined shared_ptr type as argument.