Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static polymorphism: How to define the interface?

Below is a very simple example of what I understand as static polymorphism. The reason why I'm not using dynamic polymorphism is that I do not want to obstruct inlining of functions of PROCESSOR in op.

template <class PROCESSOR>
void op(PROCESSOR* proc){
    proc->doSomething(5);
    proc->doSomethingElse();
}

int main() {
    ProcessorY py;
    op<ProcessorY>(&py);
    return 0;
}

The problem with this example is: There exists no explicit definition of what functions a PROCESSOR has to define. If one is missing, you will just get a compile error. I think this is bad style.

It also has a very practical drawback: On-line assistance of IDEs cannot of course show you the functions that are available on that object.

What is a good/official way to define the public interface of a PROCESSOR?

like image 221
Michael Avatar asked Sep 29 '22 03:09

Michael


2 Answers

There exists no explicit definition of what methods a PROCESSOR has to define. If one is missing, you will just get a compile error. I think, this is bad style.

It is. It is not. It may be. It depends.

Yes, if you want to define behavior in this way, you may want to also restrict the content, that template parameters should have. Unfortunately, it is not possible to do this "explicitly" right now.

What you want is constraints and concepts feature, that was supposed to appear as part of C++ 11, but was delayed and is still not available as of C++ 14.

However, getting compile-time error is often the best way to restrict template parameters. As an example, we can use std library:

1) Iterators.

C++ library defines few types of iterators: forward_iterator, random_access_iterator and others. For each type, there is a set of properties and valid expressions defined, that are guaranteed to be available. If you used iterator, that is not fully compatible with random_access_iterator in container, that requires random_access_iterator, you will get compiler error at some point (most likely, when using dereference operator ([]), which is required in this iterator class).

2) Allocators.

All containers in std library use allocator to perform memory allocation/deallocation and objects construction. By default, std::allocator is used. If you want to exchange it with your own, you need to ensure, that it has everything, that std::allocator is guaranteed to have. Otherwise, you will get compile-time error.

So, until we get concepts, this is the best and most widely used solution.

like image 116
Mateusz Grzejek Avatar answered Oct 12 '22 09:10

Mateusz Grzejek


First, I think there is no problem with your static polymorphism example. Since it's static, i.e. compile-time resolved, by definition it has less strict demands regarding its interface definition.

Also it's absolutely legitimate that the incorrect code just won't compile/link, though a more clear error message from the compiler would be nicer.

If you, however, insist on interface definition, you may rewrite your example the following way:

template <class Type>
class Processor
{
public:
    void doSomething(int);
    void doSomethingElse();
};

template <class Type>
void op(Processor<Type>* proc){
    proc->doSomething(5);
    proc->doSomethingElse();
}

// specialization
template <>
class Processor<Type_Y>
{
    // implement the specialized methods
};

typedef Processor<Type_Y> ProcessorY;

int main() {
    ProcessorY py;
    op(&py);
    return 0;
}
like image 30
valdo Avatar answered Oct 12 '22 07:10

valdo