Reputation: 1902
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
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
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