Bad design [C++ & pure virtuals] ?

Started by
4 comments, last by ToohrVyk 15 years, 3 months ago
So I'm shelling out some stuff and I've come across a problem which is making me think that the process flow is not working so well.

typedef std::map<std::string, std::string> properties;

struct base {
  properties props;
  base() {
    // I know this doesn't compile.. read on..
    props = get_defaults();
  }

  virtual properties get_defaults() const = 0;
};

struct derived : base {
  virtual properties get_defaults() const {
    properties def;
    def["hello"] = "world";
    def["answer"] = "42";
    return def;
  }
};

basically, I want base::props to be initialised to be whatever any derived classes get_defaults implementation returns. I see why this is not possible to do as above (calling get_defaults() inside the base implementation - because at that stage, none of the derived implementations are know, and even if they were one would need to be explicit about which implementation to call) so my first inkling is to privatise the no-arg constructor, and declare a constructor which accepts a properties object, then derived classes must explicitly invoke this constructor, but this creates a bit of a ripple effect in that every derivation must facilitate this construction call-chain. What I was wondering is if anyone has had a similar goal and achieved it in a less obtrusive (or more implicit) way? For now I will go with the inkling but am interested to see any other solutions. [FYI I originally had the get_properties() be lazy, if the properties had not been set then the get_defaults() was called, however for the sake of reducing complexity and mutex locking, I abandoned this method)]
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Advertisement
If you use a virtual base, you don't need to have every derived class to have forwarding constructors for the arguments. Ex:
class A {  public:    void print(void) { std::cout << data_ << std::endl; }    virtual ~A() {}  protected:    A(int data) : data_(data) {}  private:    int data_;};struct B : virtual A {  B() : A(0) {}};struct C : B {  C() : A(1) {}};int main(int argc, char **) {  B b;  b.print();  C c;  c.print();    return 0;}
Quote:Original post by SiCrane
If you use a virtual base, you don't need to have every derived class to have forwarding constructors for the arguments. Ex:
class A {  public:    void print(void) { std::cout << data_ << std::endl; }    virtual ~A() {}  protected:    A(int data) : data_(data) {}  private:    int data_;};struct B : virtual A {  B() : A(0) {}};struct C : B {  C() : A(1) {}};int main(int argc, char **) {  B b;  b.print();  C c;  c.print();    return 0;}

magnificent. I hate it when I over complicate things.

Thank you Don SiCrane. I am indebted to your family. Shall I leave the gun and take the Cannoli?
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
class derived;
template<class T>
class base
{
base()
{
T* t = static_cast<T*>(this);
// then you can do whatever you want.
}
};

class derived : public base<derived>
{
/// ...
};
MSN:xoyojank#live.com
Quote:Original post by xoyojank
base()
{
T* t = static_cast<T*>(this);
// then you can do whatever you want.
}

Interesting proposition, that did cross my mind but (Note that I am neither a Standards nor a C++ expert) I was unable to easy my suspicions that this has not been completely constructed at that stage and downcasting inside the constructor may lead to BadThings(TM) ?
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:Original post by silvermace
Quote:Original post by xoyojank
base()
{
T* t = static_cast<T*>(this);
// then you can do whatever you want.
}

Interesting proposition, that did cross my mind but (Note that I am neither a Standards nor a C++ expert) I was unable to easy my suspicions that this has not been completely constructed at that stage and downcasting inside the constructor may lead to BadThings(TM) ?


Indeed, it wouldn't work because of that. In practice, since the vptr for the derived class is only inserted in the class when the derived class constructor starts (on most implementations) there's no way to call a virtual function from the derived class until that class has started constructing.

What is possible using the self-referent pattern, however, is:

// Our base classclass base{  properties config;public:  base(const properties &config) : config(config) {}};// An intermediary helper classtemplate <typename T>class base_with_default : public base{public:  base_with_default() : base(T::default_config()) {}  base_with_default(const properties &config) : base(config) {}};// You can now both forward a configuration and specify a defaultclass example : public base_with_default<example>{public:  example() {}  example(const properties &config) : base_with_default(config) {}  static properties default_config() { /* ... */ }};

This topic is closed to new replies.

Advertisement