Literal operator templates for strings

The C ++ 11 standard introduced such a thing as user literals into the language [1] . Specifically, a dozen options for defining the operator "" , adding a little syntactic sugar, all, with the exception of one - a template variant:

template  type operator "" _op();

This option differs in that it not only gives an alternative option for calling a function using a suffix, but allows you to define your own rules for parsing the passed argument at the compilation stage, thereby expanding the capabilities of the compiler.

For instance:

auto x = 10001000100011001001001010001000_b;

However, there was a slight omission in the development of the standard - the template version of the user literal allows you to work only with numeric arguments, despite the fact that they are parsed character-by-character.
Such an omission, of course, could not have remained unnoticed, and at the stage of approval of the C ++ 14 standard, a solution was proposed for string arguments [2]

template  type operator "" _op();

It was soon implemented in GCC [3] and clang (GNU extension) compilers . However, the final edition of the C ++ 14 standard did not make it. However, let's not despair, there is hope that C ++ 17 will please us. In the meantime, let's see how it will be possible to apply a new type of user literals.

Define a meta-string template:

template
struct str {
    static constexpr const char value[sizeof...(Chars)+1] = {Chars...,'\0'};
    static constexpr int size = sizeof...(Chars);
};
template
constexpr const char str::value[sizeof...(Chars)+1];

Define our meta-string literal generator:

template
constexpr str operator"" _s()
{
    return str();
}

Create a map-like template for a data structure, with types as keys:

template
struct field {
    using key = Key;
    using type = Type;
    type value;
};
template
struct field_by_type;
template
struct field_by_type,Tail...>, N> {
    static constexpr int value = N;
};
template
struct field_by_type, N> : field_by_type,N+1> {};
template
struct record {
    using tuple_type = std::tuple;
    template
    typename std::tuple_element::value,tuple_type>::type::type& operator[](Key) {
        return std::get::value>(data).value;
    }
    template
    const typename std::tuple_element::value,tuple_type>::type::type& operator[](Key) const {
        return std::get::value>(data).value;
    }
    tuple_type data;
};

Since we are going to use meta strings as key types, we add a bit of input / output:

template
std::ostream& operator<< (std::ostream& os, const field f){
    os << Key::value << " = " << f.value << "\n";
    return os;
}
template
struct print_tuple {
    std::ostream& operator() (std::ostream& os, const std::tuple& t) {
        os << std::get(t);
        return print_tuple{}(os,t);
    }
};
template
struct print_tuple<0, Ts...> {
    std::ostream& operator() (std::ostream& os, const std::tuple& t) {
        return os;
    }
};
template
std::ostream& operator<< (std::ostream& os, const record& r) {
    os << "{\n";
    print_tuple{}(os,r.data);
    os << "}";
    return os;
}

Well, now the example itself:
using Person = record<
        field,
        field,
        field
    >;
int main(){
    Person p;
    p["id"_s] = 10;
    p["first_name"_s] = "John";
    p["last_name"_s] = "Smith";
    std::cout << p << "\n";
}

We can also inherit by adding new functions:
class Person
  : public record<
        field,
        field,
        field
    >
{
    public: void set_name(const std::string& f,const std::string& l) {
        (*this)["first_name"_s] = f;
        (*this)["last_name"_s] = l;
    };
};
int main(){
    Person p;
    p["id"_s] = 10;
    p.set_name("John","Smith");
    std::cout << p << "\n";
}

The resulting objects statically infer the type of the field by the given key. And when using an invalid key, they not only generate a compilation error, but they can also fill in the clang compiler:



References


  1. User-defined literals (cppreference)
  2. N3599 Literal operator templates for strings (Richard Smith)
  3. [C ++ 1y] Support n3599 - Literal operator templates for strings for C ++ 1y (GCC Project)

Also popular now: