• Advertisement
Sign in to follow this  

Bad design [C++ & pure virtuals] ?

This topic is 3408 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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)]

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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>
{
/// ...
};

Share this post


Link to post
Share on other sites
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) ?

Share this post


Link to post
Share on other sites
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 class
class base
{
properties config;
public:
base(const properties &config) : config(config) {}
};

// An intermediary helper class
template <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 default
class example : public base_with_default<example>
{
public:
example() {}
example(const properties &config) : base_with_default(config) {}
static properties default_config() { /* ... */ }
};

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement