2

Suppose I have a template struct like

template <typename T> struct Parser {
  //specializations are expected to have these methods
  inline static const byte* objectEnd(const byte* begin, const byte* end);
  inline static const byte* tryRead(const byte* begin, const byte* end, T* obj);
}

It is used in different classes, some of which are also template classes (so they may not have full info about what class they pass to Parser as a parameter).
Implementing Parser for any given type is easy, like this:

template<> struct Parser<text_line> {
  inline static const byte* objectEnd(const byte* begin, const byte* end){
    return helper::NextByteOrEnd<'\n'>(begin, end);
  }
  inline static const byte* tryRead(const byte* begin const byte* end, text_line* obj){
    const byte* lf = objectEnd(begin, end);
    obj = text_line(begin, lf);
    return lf==end ? lf : lf+1;
  }
}

I can specialize Parser for a family of templates, as well:

template<int sz> struct Parser<be_uint<sz>> {
  inline static const byte* objectEnd(const byte* begin, const byte* end){
    return begin+be_uint<sz>::size > end ? begin : begin+be_uint<sz>::size;
  }
  inline static const byte* tryRead(const byte* begin const byte* end, be_uint<sz>* obj){
    if(begin + be_uint<sz>::size > end) return begin;
    obj = be_uint<sz>::read(begin);
    return begin + be_uint<sz>::size;
  }
}

But what if I want to specialize Parser for some other set of types? Suppose I have a type predicate, say, std::is_base_of<MyHeader, T> and I want to write something like

//not actual code - how would I write this?
template<typename Header> struct Parser<Header> {
  static_assert(std::is_base_of<MyHeader, Header>::value, "Wrong!");
  inline static const byte* objectEnd(const byte* begin, const byte* end){
    //MyHeader-specific code here
  }
  inline static const byte* tryRead(const byte* begin const byte* end, Header* obj){
    //MyHeader-specific code here
  }
}

Adding optional parameters to the initial definition of Parser, like

template <typename T, bool MyHeaderFlag = std::is_base_of<MyHeader, T>::value>
struct Parser { //...

doesn't seem like a good idea - I (or any other programmer) may want to use other predicates, say, my_types::is_scientific<T>.
The problem seems like something you could resolve with SFINAE (there's a problem of what should happen if one type fits several predicates - I prefer that it wouldn't result in compiler error, but it isn't very important since such Parser specializations are likely to be palced in .cpp-files). But I couldn't find a way to do this.

Just to be clear, there is no constraint on preserving the exact definition of `Parser' or its methods (or even it being a template), as long as client code can write something like

Parser<TypeArg>::objectEnd(block.begin, block.begin+block.size)

without caring what TypeArg actually is.

Abstraction
  • 1,108
  • 11
  • 25
  • Don't store template functions in cpp-files: https://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file – Jonas Jun 07 '17 at 10:26
  • 1
    @Jonas If I write the whole template specialization (_including declaration_) in .cpp file, how exactly that is wrong? – Abstraction Jun 07 '17 at 10:55

1 Answers1

2

If you want to make specialization for some set of types, best way is to add second template parameter to your base template.

You can do that with bool, or with actually type and use std::enable_if in specializations.

I prefer the second way.

template <typename T, typename = void> struct Parser {
  //specializations are expected to have these methods
  inline static const byte* objectEnd(const byte* begin, const byte* end);
  inline static const byte* tryRead(const byte* begin, const byte* end, T* obj);
}

and specialization example

template<typename Header>
struct Parser<Header, typename std::enable_if<std::is_base_of<MyHeader, Header>::value>::type> {
  inline static const byte* objectEnd(const byte* begin, const byte* end){
    //MyHeader-specific code here
  }
  inline static const byte* tryRead(const byte* begin, const byte* end, Header* obj){
    //MyHeader-specific code here
  }
}
ForEveR
  • 55,233
  • 2
  • 119
  • 133