I had some time (and a good liquor to come along with it) to think more about the problem. Here is what I came up with:
namespace _details { struct PassedCheck { constexpr static int printError () { return 0; //no error concept check passed } }; template<template<typename> class ConceptCheck, typename ...ModelTypes> struct check_concept_impl; template<template<typename> class ConceptCheck, typename FirstType, typename ...ModelTypes> struct check_concept_impl<ConceptCheck, FirstType, ModelTypes...> : mpl::eval_if< typename ConceptCheck<FirstType>::type, check_concept_impl<ConceptCheck, ModelTypes...>, mpl::identity<ConceptCheck<FirstType>>> { }; template<template<typename> class ConceptCheck, typename LastType> struct check_concept_impl<ConceptCheck, LastType> : mpl::eval_if<typename ConceptCheck<LastType>::type, mpl::identity<PassedCheck>, mpl::identity<ConceptCheck<LastType>>> { }; } template<template<typename> class ConceptCheck, typename ...ModelTypes> struct check_concept { private: typedef typename _details::check_concept_impl<ConceptCheck, ModelTypes...>::type result_type; public: // the constexpr method assert produces shorter, fixed depth (2) error messages than a nesting assert in the trait solution // the error message is not trahsed with the stack of variadic template recursion constexpr static int apply() { return result_type::printError(); } }; template<typename ContainerType> struct IsContainerCheck : is_container<ContainerType> { template<typename BoolType = false_t> constexpr static int printError () { static_assert(BoolType::value, "Type is not a container model"); return 0; } };
and use:
check_concept<IsContainerCheck, std::vector<int>, std::vector<int>, float, int>::apply();
The solution is probably not the most elegant, but I keep a short assert message:
It is included in the file from .. /main.cpp:407:../constraint.check.hpp: when creating 'static constexpr int IsContainerCheck :: printError () [with BoolType = std :: integral_constant; ContainerType = float]: .. /constraint.check.hpp:61:34: required from 'static constexpr int check_concept :: apply () [with ConceptCheck = IsContainerCheck; ModelTypes = {std :: vector>, std :: vector>, float, int}] .. /main.cpp:25:83: required from here .. /constraint.check.hpp:74//: error: static statement failed: Type is not a container model static_assert (BoolType :: value, "Type is not a container model");
The statement is issued in the constexpr method after the check_concept template specification has been completed. Attaching a static statement directly to the template class definition would drag the entire check_concept_impl recursion cycle into the error message.
Therefore, changing the IsContainerCheck property to something like (the rest of the changes are omitted for reading):
template<typename ContainerType> struct IsContainerCheck { static_assert(is_container<ContainerType>::type::value, "Type is not a container model"); };
will result in an error
../constraint.check.hpp: when creating 'struct IsContainerCheck: .. /constraint.check.hpp:36:9: required from' struct _details :: check_concept_impl / usr / include / boost / mpl / eval _if.hpp: 38:31: required from struct boost :: mpl :: eval_if, _details :: check_concept_impl, boost :: mpl :: identity →> ../constraint.check.hpp:36:9: required from 'struct _details :: check_concept_impl >, float, int> /usr/include/boost/mpl/eval_if.hpp:38:31: required from struct boost :: mpl :: eval_if, _details :: check_concept_impl>, float, int>, boost :: mpl: : identity →> ../constraint.check.hpp:36:9: required from 'struct _details :: check_concept_impl>, std :: vector>, float, int> ../constraint.check.hpp:53:84: required from 'struct check_concept>, std :: vector>, float, int> ../main.cpp:25:81: required from here .. /constraint.check.hpp:72:2: error: static statement failed Eno: Type is not a container model static_assert (is_container :: type :: value, "Type is not a container model");
As you can see, each recursive call to eval_if is corrected in the error description, which is bad because it makes the error message dependent on the number and type of template parameters.