3

I have this particular case and would need some opinion on some of the design aspects.

Basically, I have already defined classes ( which represents position in different spaces ) and the classes does not have a concrete relationship to each other.

So, I designed a template based interpolator which can work on the currently available position representing classes.

Roughly like,

template<typename TPoint>
class Interpolator
{
 .....

some function
{
TPoint::CalculateCriticalAxis(point);
}
}

As you can see, there are some static functions defined in all position classes that can be accessed inside the interpolator. So, now since somebodyelse who need to use the interpolator and define a new position(point) class, will have to know that he needs to define them by looking at the code since there is no base class for positions. The question is how can I design a base class which will also contain static methods which user have to override. As I understand static methods can not be overridden. So, what is the easiest way to force implementing them if somebody want to define a new position(point) class. I do not want to redesign it since there are legacy position classes that are not from me and they non related in some sense. Thanks!

  • 1
    What you're asking for is similar to the Concept proposal for a future version of the C++ language. A library offering functionality in that space is [boost Concept Checks](http://www.boost.org/doc/libs/1_58_0/libs/concept_check/using_concept_check.htm). Alternatively - for a simpler but less powerful solution - you could use a [check for `static` member](http://stackoverflow.com/questions/23133683/how-to-detect-the-presence-of-a-static-member-function-with-certain-signature) with [`static_assert`](http://en.cppreference.com/w/cpp/language/static_assert). – Tony Delroy May 07 '15 at 07:08
  • Thanks. I will look into that. Also, say i do not intend others to define new position classes, then designing it this way is within OOP or should I still go level higher and introduce a base class for already available classes. – code_not_yet_complete May 07 '15 at 07:24
  • 1
    Related to [does-static-polymorphism-make-sense-for-implementing-an-interface](http://stackoverflow.com/questions/20771210/does-static-polymorphism-make-sense-for-implementing-an-interface). – Jarod42 May 07 '15 at 07:34
  • 1
    *"designing it this way is within OOP"*? - I wouldn't really call it an OOP design... you're using static polymorphism and only using a class to group static functions - no inheritance, polymorphism, encapsulation. Anyway, no particular reason you should use OOP as some kind of goal or standard either. I don't see any particular reason to introduce a base class - most people just use a combination of documentation and letting the compile fail if an inappropriate `TPoint` class is provided as a termplate parameter... a static assert or two is already cleaning that up considerably. – Tony Delroy May 07 '15 at 07:36
  • 1
    Simple way (but of course not the best) is to document it somehow. I guess nobody will use some library without looking at examples or documentation. Even if someone will try to compile it with wrong point type, he will recive error ``error: ‘check’ is not a member of ‘point2_t’ TPoint::check(p);``. However it is better to use some kind of check, suggested by others. – Nikolay K May 07 '15 at 08:21

1 Answers1

1

Use a static member function, defined as deleted. A [[deprecated( "message" )]] attribute allows you to print an error message when someone tries to access a missing implementation.

// May be, but doesn't need to be a template.
struct base_interface {
    // Likewise, this could be templated.
    [[deprecated( "Derived class must override calculation." )]]
    static value_type calculate_something() = delete;

    // "Public" interface, in the vein of the non-virtual idiom (NVI).
    // This must be a template, and it can't be a member - it's a friend.
    template< typename derived >
    friend value_type something_of( derived const & o )
        { return o.calculate_something(); }
};

The well-known weakness of "duck typing" is that the user might not try to access the missing implementation. This is a different problem. Any solution amounts to accessing all the aspects of a proper derived class.

A base class can do this, but only carefully, because there are some issues:

  1. The derived class will be incomplete within the definition of its base class. But, it will be complete within the definitions of member functions of the base class.

  2. The existence of a complete derived class implementation should be verified, but you don't actually want to instantiate all parts of a derived class template, much less link it into the binary. "Too much" usage will bloat compile times and executable size, respectively.

One solution is to use static_assert and decltype inside a CRTP base class constructor.

// CRTP template, derived class must pass itself to base.
template< class derived >
class base_interface {
    base_interface() {
        static_assert ( std::is_same< decltype( derived::calculate_something() ),
                                      typename derived::value_type >::value,
            "derived class is missing calculate_something" );
    }

    // Just enough to allow the static_assert condition to evaluate as false.
    static struct invalid calculate_something();
    typedef void value_type;
};

http://coliru.stacked-crooked.com/a/b2c5f9bf8ed58a09

Note that this is a completely different solution from the first one. This prevents the CRTP base from being trivially default constructible, but that's a fairly small price to pay. Alternately, you could put the static_asserts in a different function that is sure to be accessed, and retain the trivial default constructor.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421