1

I have something like

struct Base{
  int x=0;
};

And I want all the children to have a static variable "myint" so that, when built, they cumulate in x the values of myint for all parents and themselves. (In reality in my case x is a set and what i want is the union of the sets of each children).

struct Derived : public Base {
  static int myint =1;
  Derived(){x+=myint;}
};

struct Derived2 : public Derived {
  static int myint = 2;
  Derived2(){x+=myint;}
};

So that x=1 for Derived and x=3 for Derived2.

I've seen that something like this is possible with CRTP (static variable for each derived class) writing a BaseX class:

template<class A>
struct BaseX : public Base {
  static int myint;
  BaseX() {Base::x+=myint;}
}

struct Derived : public BaseX<Derived>;

But such pattern can't be applied for the second level of inheritance. I tried with multiple inheritance but what I obtain, of course, is that I have two values for x, each one with the wrong value (say x=1 for the part inheriting from Derived, and x=2 for the part deriving from BaseX).

Do you see any solution for this problem without having to call for (x+=myint) in all constructor and define myint in each derived?

Teloze
  • 279
  • 2
  • 8
  • smells a bit like a [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). What is that `x` good for? Does really need each instance of `Derived` its own copy of it? What can you do with `x` that you cannot do already with `myint` ? – 463035818_is_not_an_ai Sep 20 '17 at 14:46
  • well, no. Not each instance. X can be static as well. Actually it corresponds to the set of "keywords" allowed for that particular class. Each class has its own keywords, plus all those of its parents. – Teloze Sep 20 '17 at 14:51
  • The methods in Base check if the keywords are allowed and just save the value provided. – Teloze Sep 20 '17 at 14:53
  • Also I just want to add that it will rarely occurs to have multiple instances of the same class – Teloze Sep 20 '17 at 15:10

3 Answers3

2

Variation for not-int/enum types:

#include <iostream>
#include <set>
#include <string>

struct Base {
    std::set<std::string> x;
};

template <class BASE>
struct Derived : BASE {
    static const std::string my_str;
    Derived() {
        this->x.insert(my_str);
    }
};

struct Derived1 : Derived<Base> {};
template<>
const std::string Derived<Base>::my_str = "derived";

struct Derived2 : Derived<Derived1> {};
template<>
const std::string Derived<Derived1>::my_str = "derived2";

int main() {
    Derived1 d1;
    Derived2 d2;

    for (const auto & word : d1.x) {
        std::cout << word << std::endl;
    }

    for (const auto & word : d2.x) {
        std::cout << word << std::endl;
    }
}
  • I feel like a pain in the ** in saying this but in this solution I believe all derived share the same static variable. Suppose to write struct Derived1B : public Derived; There's no way to specialize Derived1::...::my_str="Derived" and Derived1B::...::my_str="DerivedB" – Teloze Sep 20 '17 at 16:05
  • very good idea. Probably it can be generalized with some helper structure. I'll give it a try. Thank you very much! – Teloze Sep 20 '17 at 16:40
0

I would do it in this way:

#include <iostream>

struct Base {
    int x = 0;
};

template <class BASE, int MY>
struct Derived : BASE {
    static const int myint = MY;
    Derived() {
        this->x += myint;
    }
};

struct Derived1 : Derived<Base, 1> {};
struct Derived2 : Derived<Derived1, 2> {};

int main() {
    Derived1 d1;
    Derived2 d2;
    std::cout << d1.x << std::endl;
    std::cout << d2.x << std::endl;
}
  • well, in the case of int it is indeed a good solution. However, the actual type of "x" is a std::set and, AFAIK, there's no way to pass particular instances of a type other than enum and int as template parameter. – Teloze Sep 20 '17 at 15:22
0

I have to thank Toxa Evtushenko for the help. Thanks to his idea I came up with this variation which I believe can be applied in a general way:

#include <iostream>
#include <set>
#include <string>

struct Base {
  std::set<std::string> x;
};

template<class DERIVED>
struct help{
  static const std::string my_str;
};

template <class BASE, class DERIVED>
struct Derived : BASE {
private:
  help<DERIVED> val;
public:
  Derived() {
    this->x.insert(val.my_str);
  }
};

struct Derived1a;
template<> const std::string help<Derived1a>::my_str = "Derived1A";

struct Derived1a : Derived<Base,Derived1a> {};

struct Derived1b;
template<> const std::string help<Derived1b>::my_str = "Derived1B";

struct Derived1b : Derived<Base,Derived1b> {};

struct Derived2;
template<> const std::string help<Derived2>::my_str = "Derived2";
struct Derived2 : Derived<Derived1a,Derived2> {};

int main() {
  Derived1a d1a;
  Derived1b d1b;
  Derived2  d2;

  for (const auto & word : d1a.x) {
    std::cout << word << std::endl;
  }

  std::cout << std::endl;

  for (const auto & word : d1b.x) {
    std::cout << word << std::endl;
  }

  std::cout << std::endl;

  for (const auto & word : d2.x) {
    std::cout << word << std::endl;
  }
}

Of course more elegant solutions are welcome!

Teloze
  • 279
  • 2
  • 8