I'm modifying a class by adding a data-member that is templated. The code will invariably be using a default template type for the template parameter in all invocations, except for one place. Partially motivated by this, and partially due to a a desire/whim, I do not want to add a template parameter to the class as shown below:
#include <iostream>
template <typename Der>
class A : private Der {
public:
int get() { return Der::get_(); }
};
class B {
protected:
int get_() { return 20; }
};
class C {
protected:
int get_() { return 30; }
};
using Default = B;
template <class T = Default>
class User {
public:
User() {}
A<T>& getMember() { return m_; }
private:
A<T> m_; // This is what I am adding, and exposing param T.
};
int main(int argc, char* argv[]) {
User h;
std::cout<<h.getMember().get()<<std::endl;
}
One solution that I could think of is the use of a sum-type, but this introduces some exception handling code:
class User {
public:
User() : m_(A<Default>()) {}
template <typename T>
User(const T& in) : m_(in) {}
template <typename T>
A<T>& getMember() { return std::get<A<T>>(m_); }
private:
std::variant<A<B>, A<C>> m_;
};
I'm looking for:
A name for what I am trying to do, if it exists.
Ways in which I can accomplish what I desire.
This is not an answer, as I don't find the virtual function call completely satisfactory, but, as progress, another option I can share is type-erasure using an inner base class.
Something like:
#include <iostream>
#include <memory>
class User {
public:
template <class T>
User(T&&) : m_(new Internal<T>()) {}
int doOnMember() { return m_->doOnMember(); }
private:
class InternalBase {
public:
virtual int doOnMember() = 0;
};
template <typename T>
class Internal : public InternalBase {
public:
Internal() {}
int doOnMember() { return m_.get(); }
private:
A<T> m_;
};
private:
std::shared_ptr<InternalBase> m_;
};
int main(int argc, char* argv[]) {
User h(B{});
std::cout<<h.doOnMember()<<std::endl;
User h1(C{});
std::cout<<h1.doOnMember()<<std::endl;
}
Related
I'm struggling with a problem regarding the linking between two derived classes from a template one. Let's say that we have a template base class Param_B which take as parameter one of it's children (Param_S or Param_M). The template parameter of the class Param_B is used further for definition of other local variables( _parent and a callback function _func). For me is necessary to have the Param_S or Param_M for dynamically call of the function.
The problem is that after I create the pointers to the children's and add timers and functions to them I want to have a _link Variable which will enable to exchange information between both children types. E.g. from the Param_S I want to change the timeout of a Param_M timer by using the _link variable. I
#include <stdio.h>
#include <memory>
#include <vector>
class Param_S;
class Param_M;
template <class T>
class Functionn{
Functionn(){};
~Functionn(){};
private:
std::function<void(std::shared_ptr<T>)> _callback;
};
template <class T>
class Timer
{
public:
Timer(){ timeout = 0; };
~Timer(){};
void setTimeout(const int time){ timeout = time; };
std::weak_ptr<Functionn<T>>& getFunction(){ return _func; };
std::weak_ptr<T> getParent(){ return _parent;};
private:
int timeout;
std::weak_ptr<T> _parent;
std::weak_ptr<Functionn<T>> _func;
};
template <class T>
class Param_B {
public:
Param_B(){};
~Param_B(){};
std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
return _timers;
}
void addTimer(std::shared_ptr<Timer<T>> _t){
_timers.push_back(std::move(_t));
}
std::weak_ptr<Param_B> getLink(){
return _link;
}
void setLink(std::weak_ptr<Param_B> link){
_link = link;
}
private:
std::weak_ptr<Param_B> _link;
std::vector<std::shared_ptr<Timer<T>>> _timers;
};
class Param_S : public Param_B<Param_S>
{
public:
Param_S(){ paramS = -2; };
~Param_S(){};
void setValue(){
paramS = 33;
}
int getValue(){ return paramS;};
private:
float paramS;
};
class Param_M : public Param_B<Param_M>
{
public:
Param_M(){ parammM = -4; };
~Param_M(){};
int getValue(){ return parammM; };
private:
int parammM;
};
class sys{
public:
std::vector<std::shared_ptr<Param_S>> getParams(){
return _params;
}
private:
std::vector<std::shared_ptr<Param_S>> _params;
};
class module{
public:
std::vector<std::shared_ptr<Param_M>> getParams(){
return _params;
}
private:
std::vector<std::shared_ptr<Param_M>> _params;
};
void main(){
std::shared_ptr<sys> _sys = std::make_shared<sys>();
_sys->getParams().push_back(std::make_shared<Param_S>());
_sys->getParams()[0]->addTimer(std::make_shared<Timer<Param_S>>());
std::shared_ptr<module> _mod = std::make_shared<module>();
_mod->getParams().push_back(std::make_shared<Param_M>());
_mod->getParams()[0]->addTimer(std::make_shared<Timer<Param_M>>());
_sys->getParams()[0]->setLink(_mod->getParams()[0]); // this not work , the conversion failed
}
Thank you for your help.
You need to have a non-template base for Param_B. It can't involve anything related to the template parameter, but you could have virtual functions here
class Param_B_Base {
public:
virtual ~Param_B_Base() = default;
std::weak_ptr<Param_B_Base> getLink(){
return _link;
}
void setLink(std::weak_ptr<Param_B_Base> link){
_link = link;
}
// an example of possible behaviour
virtual void setTimeouts(const int time) = 0;
// another example of possible behaviour
virtual void invokeHandlers() = 0;
private:
std::weak_ptr<Param_B_Base> _link;
};
template <class T>
class Functionn{
void operator()(std::shared_ptr<T> ptr) { _callback(ptr); }
private:
std::function<void(std::shared_ptr<T>)> _callback;
};
template <class T>
class Timer
{
public:
void setTimeout(const int time){ timeout = time; };
void invoke() { if (auto func = _func.lock()) { if (auto parent = parent.lock() { (*func)(parent); } } }
private:
int timeout = 0;
std::weak_ptr<T> _parent;
std::weak_ptr<Functionn<T>> _func;
};
Then you can have a template for subclasses
template <class T>
class Param_B : public Param_B_Base {
public:
std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
return _timers;
}
void addTimer(std::shared_ptr<Timer<T>> _t){
_timers.push_back(std::move(_t));
}
// The implementation of the virtual methods can know about T
void setTimeouts(const int timeout) override {
for (std::shared_ptr<Timer<T>> timer : _timers) {
timer->setTimeout(timeout);
}
}
void invokeHandlers() override {
for (std::shared_ptr<Timer<T>> timer : _timers) {
timer->invoke();
}
}
private:
std::vector<std::shared_ptr<Timer<T>>> _timers;
};
I would suggest making the link between the two derived classes type-agnostic (i.e. make the link a std::function rather than a pointer to an instance of the other type.
If you would like to keep it as a pointer, your problem is in your use of Param_B within the Param_B template class.
void setLink(std::weak_ptr<Param_B> link)
In the above line, Param_B will refer to the Param_B<Param_M> when the method is called on an instance of Param_B<Param_M>, and it will refer to Param_B<Param_S> when the method is called on an instance of Param_B<Param_S>.
What you need to do is add a second template parameter to Param_B to tell it what the linked derived type is.
template <class T, class L>
class Param_B {
public:
Param_B(){};
~Param_B(){};
std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
return _timers;
}
void addTimer(std::shared_ptr<Timer<T>> _t){
_timers.push_back(std::move(_t));
}
std::weak_ptr<Param_B> getLink(){
return _link;
}
void setLink(std::weak_ptr<Param_B<L, T>> link){
_link = link;
}
private:
std::weak_ptr<Param_B<L, T>> _link;
std::vector<std::shared_ptr<Timer<T>>> _timers;
};
Then you'll need a forward declaration of Param_M so it can be a template parameter for Param_S.
class Param_M;
class Param_S : public Param_B<Param_S, Param_M>
{
...
};
class Param_M : public Param_B<Param_M, Param_S>
{
...
};
Edit:
If you know how the derived types will interact, don't use functions as links; see the answer from #Caleth.
I am trying to create something similar to a type-safe multiqueue. The idea is that when I push an item, it will be added to a queue consisting of objects of the same type.
All the queues will have a common interface(the queue_intf in this case) that will do some processing.
The code I came up is the following:
#include <queue>
#include <vector>
#include <memory>
#include <stdlib.h>
class queue_intf {
public:
virtual void process(void) = 0;
};
template <typename T>
class queue : public queue_intf {
public:
std::queue<T> q_;
static const char* name()
{
return typeid(T).name();
}
virtual void process(void) override {
printf("process: %s\n", this->name());
}
void push(T &a) {
printf("push: %s\n", this->name());
}
};
template <typename...>
class queues {
public:
std::vector<queue_intf *> qs_;
void process(void) {
for (auto q: this->qs_) {
q->process();
}
}
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
queue<T> q_;
queues() {
this->qs_.push_back(&this->q_);
}
void push(T &v) {
q_.push(v);
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.process();
qs.push(bi);
}
However when I compile it I get the following error:
main.cc: In function ‘int main(int, char**)’:
main.cc:65:15: error: no matching function for call to ‘queues<a, b>::push(b&)’
qs.push(bi);
^
main.cc:45:10: note: candidate: void queues<T, Ts ...>::push(T&) [with T = a; Ts = {b}]
void push(T &v) {
^~~~
main.cc:45:10: note: no known conversion for argument 1 from ‘b’ to ‘a&’
I was expecting the queue class to have void push(b &v) method, but it seems it doesn't.
Any idea why?
Edit:
Here is a smaller example (no std:vector or anything):
template <typename...>
class queues {
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
void push(T &v) {
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.push(bi);
}
To explain this, let's use a simplified example, you have just two classes:
class base {
public:
void method(int *);
};
class derived : public base {
public:
void method(char *);
};
And you attempt to call it:
derived d;
int i;
d.method(&i);
This fails. Name lookup starts at the given derived class, and finds a matching class method with the specified name: method. However its parameter doesn't match, so this is ill-formed. If the derived class had both methods, overload resolution will pick the matching one. But once a class method with the specified name is found any parent class will not be searched for any other methods with the same name. Only if the derived class does not have any methods with the specified name does name lookup continue with the parent class(es).
Putting
using base::method;
In the derived class imports base's method into the derived's namespace, at which point everything works just like with regular overload resolution.
This is why you need a using declaration with your recursive templates. The second derived template imports it from the first one, the next one imports everything from the second derived template, and so on.
I would like to instantiate all States in the MachineT as a shared_ptr<T> then access them by typename.
In the following code, it refers to the instantiation (MachineT constructor) and a way to access the states (get function).
Is there any hashmap trick or a way to store in the class an "Index" information such as StateA::Index?
#include <memory>
#include <vector>
template <typename... States>
class MachineT {
public:
MachineT() {
states_.resize(sizeof...(States));
for (unsigned i = 0; i < states_.size(); ++i) {
// Instanciate states
// states_[i].reset(new decltype(State[i])());
}
}
~MachineT() {}
class State {
State(int state_id) : state_id_(state_id) {}
const size_t state_id_;
};
template<typename T>
std::shared_ptr<T> get() {
// Retrun the shared_ptr to the State
}
std::vector<std::shared_ptr<State>> states_;
};
struct StateA; // Forward declaration
struct StateB;
using StateMachine = MachineT<StateA, StateB>;
class StateA : StateMachine::State {};
class StateB : StateMachine::State {};
int main(int argc, char const* argv[]) {
StateMachine sm;
std::shared_ptr<StateA> state_a = sm.get<StateA>();
return 0;
}
It's perfectly doable. Here's how to do it in C++14:
#include <memory>
#include <tuple>
template <typename... States>
class MachineT {
public:
MachineT()
: states_{
std::make_shared<States>()...
} {
}
~MachineT() {}
template<typename T>
std::shared_ptr<T> get() {
return std::get<std::shared_ptr<T>>(states_);
}
std::tuple<std::shared_ptr<States>...> states_;
};
struct State1 {};
int main() {
MachineT<State1> a;
a.get<State1>();
}
Equivalent of std::get can be implemented with C++11 tools
I've tried to use c++ properties and now I'm stuck with this:
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<class s, typename t>
class getonly {
protected:
friend s;
t value;
t set(); // operator =...
public:
t get(); // t operator...
};
};
class b : public a {
public:
getonly<b, int> id;
};
What I want is something like this, where getonly is parametrized by only the typename (int), and not the class (b):
class b : public a {
public:
getonly<int> id;
};
Is this possible? Can anyone help me?
It appears that you want a data member that can be read by any code, but that can only be changed by the containing class.
The attempt at providing friend-ship via template would not have compiled prior to C++11. It means that this code can not be easily modified to work with a C++03 compiler (with C++03 one could express the access restriction on modification via e.g. an owner credential argument to a setter). However, that concern becomes less significant every day.
Here's your code modified to compile, and with the inheritance changed from public to private (it doesn't make much sense to say that b "is-an" a):
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<class s, typename t>
class getonly {
protected:
friend s;
t value_;
public:
auto get() const -> t const& { return value_; }
operator t const& () const { return value_; }
getonly( t v ): value_( v ) {}
};
};
class b : private a {
public:
getonly<b, int> id;
void set_id( int x ) { id.value_ = 2*x; }
b(): id( 42 ) {}
};
#include <iostream>
auto main() -> int
{
using namespace std;
b obj;
cout << obj.id << endl;
obj.set_id( 333 );
cout << obj.id << endl;
}
So first off, this can be done with CRTP
template<typename s>
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<typename t>
class getonly {
protected:
friend s;
t value;
t set(); // operator =...
public:
t get(); // t operator...
};
};
class b : public a<b> {
public:
getonly<int> id;
};
But since you mentioned inheritance, are you aware that friend declarations are not inherited? If that is your goal, then there is a more elegant solution - do not use the property as a base class but as traits:
template<typename T, typename F>
class property_impl
{
friend F;
private:
T value_;
protected:
void set(T value)
{
value_ = value;
}
public:
T get()
{
return value_;
}
};
template<typename F>
class property_traits
{
template<typename T> using property = property_impl < T, F > ;
};
class foo : public property_traits < foo >
{
public:
property<int> id_;
};
class bar : public foo, public property_traits < bar >
{
public:
property<int> another_id_;
void doStuff(foo f)
{
auto value = f.id_.get();
// f.id_.set(5); <-- fails since friend is not inherited
}
};
int main(int argc, char** argv)
{
foo f;
auto value = f.id_.get();
bar b;
auto another_value = b.another_id_.get();
b.doStuff(f);
return 0;
}
This will give you the ability to inherit and specialize on each class that needs a property while retianing the fact, that only the class type used in specialization is able to modify the value.
Again, maybe you want that, I am not sure.
template <typename T>
class BaseQueue
{
public :
virtual void push_back(T value) = 0;
//other virtual methods
};
template <typename T>
class BaseDeque: public virtual BaseQueue<T>
{
public:
virtual void push_front(T value) = 0;
//other virtual methods
};
//Realisation
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata;
public:
VectorQueue()
{
adata = array();
}
void push_back(T value)
{
adata.push_back(value);
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
VectorDeque<int> vd = VectorDeque<int>();//here is a error
int i;
std::cin >> i;
return 0;
}
I have such error: "C2259: 'VectorDeque' : cannot instantiate abstract class ...". How can I fix it? Class VectorQueue has realize every virtual method of BaseQueue class already. But the compiler doesn't know it. The only way I see is to write something like this:
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
void push_back(T value)
{
VectorQueue::push_back(value);
}
//repeat it fo every virtual method of BaseQueue class (interface)
};
But it's awful.
push_back from BaseQueue isn't implemented on the BaseDeque side of the inheritance chain, and thus the childmost class is still abstract.
I think you're trying to force a class relationship here that shouldn't exist. Note how in the standard library deque and vector are distinct container types and things like queue adapt those containers to very precise interfaces rather than trying to inherit.
Even if you solve your diamond issue (or follow #Mark B's advice and keep them separate), you have a few other issues in there:
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata; // if this is private, VectorDeque can't reach it
public:
// constructors have an initializer section
// member variables should be initialized there, not in the body
VectorQueue()
// : adata() // however, no need to explicitly call default constructor
{
// adata = array();
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>
{
void push_front(T value)
{
// if adata is protected, you can just access it. No need for scoping
/*VectorQueue::*/ adata.push_front(value);
// Error: std::vector doesn't have a method push_front.
// Perhaps you meant to use std::list?
}
};
Multiple inheritance and static polymorphism are of help, for instance:
// Abstract bases
template <typename T, typename Val>
class BaseQueue
{
public :
void push_back(Val val)
{
static_cast<T*>(this)->push_back(val);
}
// ...
};
template <typename T, typename Val>
class BaseDeque
{
public:
void push_front(Val val)
{
static_cast<T*>(this)->push_front(val);
}
// ...
};
// Concrete class
#include <deque>
template <typename Val>
class QueueDeque:
public BaseQueue<QueueDeque<Val>, Val>,
public BaseDeque<QueueDeque<Val>, Val>
{
std::deque<Val> vals;
public:
void push_front(Val val)
{
vals.push_front(val);
}
void push_back(Val val)
{
vals.push_back(val);
}
// etc..
};
int main()
{
QueueDeque<int> vd;// no more error
vd.push_front(5);
vd.push_back(0);
return 0;
}