Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you "not repeat yourself" when giving a class an accessible "name" in C++?

Tags:

c++

dry

Consider the following:

class Base {
  public:
    virtual std::string getName() = 0;
    ...
};

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    std::string getName() { return("Derived1"); }
    ...
};

class Derived2 : public Base {
  public:
    static std::string getClassName() { return("Derived2"); }
    std::string getName() { return("Derived2"); }
    ...
};

The idea is that if you have the derived class passed as, say, a template parameter, then you can get its class name via getClassName, while if you have it passed as a pointer to base class, you can get the name via getName.

I have seem a lot of similar questions to this here but all of them seem to ask stuff like "how do I use a static virtual", "why don't static virtuals exist" and various stuff like that, and the answers seem to address that more than what I think the real underlying problem is, which is: how can I avoid having to repeat myself with that code and mentioning the name twice while using as little boilerplate as possible? (Don't Repeat Yourself, or DRY Rule)

I don't want a macro, either.

like image 436
The_Sympathizer Avatar asked Aug 19 '16 05:08

The_Sympathizer


2 Answers

First off, you can re-use getClassName in getName:

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    std::string getName() override { return getClassName(); }
    ...
};

Now, all definitions of getName() are identical, so you can put them in a macro to save on typing (and make them more future-proof):

#define GET_NAME() std::string getName() override { return getClassName(); }

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    GET_NAME()
    ...
};

Or you can bundle getClassName in there as well:

#define GET_NAME(maName) \
  static std::string getClassName() { return(maName); } \
  std::string getName() override { return getClassName(); }

class Derived1 : public Base {
  public:
    GET_NAME("Derived1")
    ...
};

You say "I don't want a macro, either," but macros are a good tool for that, and I wouldn't see a single problem with using them like this. However, if that is not what you want, you can do it without them as well:

template <class Self>
struct GetName : public Base
{
  std::string getName() override { return Self::getClassName(); }
};

class Derived1 : public GetName<Derived1> {
  public:
    static std::string getClassName() { return("Derived1"); }
    ...
};

class Derived2 : public GetName<Derived2> {
  public:
    static std::string getClassName() { return("Derived2"); }
    ...
};
like image 167
Angew is no longer proud of SO Avatar answered Nov 15 '22 10:11

Angew is no longer proud of SO


Don't fear data:

class Base {
  public:
    std::string const Name;
    Base(std::string Name) : Name(Name) { }

};

class Derived1 : public Base {
  public:
    static const std::string Name;
    Derived1() : Base { Name } { }
};

const std::string Derived1::Name { "Derived1" }
like image 23
MSalters Avatar answered Nov 15 '22 08:11

MSalters