user2717954
user2717954

Reputation: 1902

avoid repeating the same code when using inheritance

Let's say I have this class

struct IDebug {
    virtual void print(std::ostream&) const = 0;
};

and the prints should follow some format defined for each class implementing IDebug.

the format is static in the sense that for a certain implementation will always have the same format and that I want the format to be used regardless of whether I have any instance of the class (for example at program initialization)

so I added a:

static std::string format(); 

to each of the implementing class. ugly (because it's not forced by the interface) but I could live with that

now I wanted to add some validation in the interface level that the actual print() is following the format this class defines. in order to do that I had to make a modification:

struct IDebug {
   void print(std::ostream& o) const { // no longer virtual
     auto format = format_impl();
     // preprocess using format
     print_impl(o);
     // postprocess using format
   }

protected:
   virtual void print_impl(std::ostream& o) const = 0;
   virtual std::string format_impl() const = 0;
};

and each of the implementing classes now have the exact same line of code:

std::string format_impl() const override { return format(); }

struct MooDebug : public IDebug { 
    // rest of the code
    static std::string format() { return "whatever"; } // this was already here. sadly the interface couldn't force it
    std::string format_impl() const override { return format(); } // all implementing classes are repeating this exact line
};

I'm looking for advice how to avoid it or make it better.

Upvotes: 0

Views: 92

Answers (2)

user2717954
user2717954

Reputation: 1902

based on @JVApen answer I ended up doing this:

template <class T>
struct IDebug {
   void print(std::ostream& o) const { // no longer virtual
     auto myformat = T::format();
     // preprocess using format
     print_impl(o);
     // postprocess using format
   }

protected:
   virtual void print_impl(std::ostream& o) const = 0;
};

now we also force (up to not using the CRTP correctly) the static implementation of format() which is a nice added bonus

Upvotes: 0

JVApen
JVApen

Reputation: 11317

You could make use of some CRTP like class to create a base class that does what you want:

struct MooDebug : DebugHelper<MooDebug>
{
static std::string format() { return "whatever"; }
};

 template<typename T>
struct DebugHelper : IDebug
 {
     std::string format_impl() const override { return T::format(); }
  };

Note that in CRTP you actually cast your this pointer to T& and call a method that way.

Alternative in C++17 (haven't tested this), you can pass values to a template. As I never did this with strings, it might not work.

 template<auto T>
struct DebugHelper : IDebug
 {

  static std::string format() { return T; } 
  std::string format_impl() const override { return T; }
  };

  struct MooDebug : DebugHelper< "whatever">
   {};

Upvotes: 1

Related Questions