mercredi 3 avril 2019

how to define constraints for SFINAE and static assert in C++11

I am experimenting on serialization/deserialization with template, and have now something working. Obviously, when implementing it, I ran into many troubles with hundreds of compiler error logs. Before extending my library further, I would like to secure it a bit, using SFINAE (which I only used scarcely so far) and static_asserts.

Not to mess up any further with my lib, I'm training in a sandbox :

http://coliru.stacked-crooked.com/a/9eb4eaefaac90fc0

I want to define a few predicates :

  • is_a_base to check the object derives from Base.
  • is_insertable & is_extractable to check if operator>> and operator<< have been defined for this type.

I want to be able to use those predicates for both SFINAE specialization and static_assert.

#include <sstream>
#include <iostream>

//forward declaration
class Base;

//"Template typedef" to check predicates
template <typename T>
using is_a_Base = typename std::enable_if<std::is_base_of<Base, T>::value, void>::type;

template <typename T, typename is = std::istream>
using is_extractable = decltype (is{} >> T{});

template <typename T, typename os = std::ostream>
using is_insertable = decltype (os{} << T{});

//Test classes
class Base{
    public:
        std::string getStr(){ return "Base.getStr()";}
};
class Derived: public Base {};

class Other{};

//A template class with its specializations with SFINAE
template <typename T, typename Enable = void>
class C{
    public:
        static void f(T& o){
            std::cout << "f<T> default !" << std::endl;
    }
};

template<typename T>
class C<T, is_a_Base<T>>
{
    public:
        static void f (T& o)
        {
            std::cout << "f<is_a_A>() ! " << o.getStr() << std::endl;
        }
};

template<typename T>
class C<T, is_insertable<T> >
{
    public:
        static void f (T& o)
        {
            std::cout << "f<is_insertable() ! " << o << std::endl;
        }
};


template<typename T>
std::string g(T& ref)
{
    //static_assert(is_a_Base<T>, "T is not a Base"); //can't figure out the syntax here
    return ref.getStr();
}



int main(){
    Base a;
    Derived b;
    int myint = 1;
    std::string str="toto";

    Other oops;


    C<Base>::f(a);
    C<Derived>::f(b);
    C<int>::f(myint);      //Not calling is_insertable ??
    C<std::string>::f(str); //Not calling is_insertable ??
    C<Other>::f(oops);

    std::cout << "g:" << g(a) << std::endl;
    //std::cout << "g:" << g(oops) << std::endl; //should be blasted by the static assert
}

Results :

f<is_a_A>() ! Base.getStr()
f<is_a_A>() ! Base.getStr()
f<T> default !
f<T> default !
f<T> default !

g:Base.getStr()

So far, is_a_base is working for SFINAE. However, is_insertable is not working for the int and string variables ? Also I could not figure out how to properly reuse the is_a_base predicate into my assert statement.

(Since I am constrained with a cross-compiler not supporting beyond C++11, I can't use benefits from C++14 and beyond.)

Aucun commentaire:

Enregistrer un commentaire