Warning: longish introduction ahead needed to explain the problem. The Named Template Argument idiom first described in ch 16.1 of Vandevoorde and Josuttis can be conveniently written with the Boost.Parameter library
#include <iostream>
#include <typeinfo>
#include <boost/parameter.hpp>
#include <boost/static_assert.hpp>
struct DefaultPolicy1 {};
struct DefaultPolicy2 {};
typedef boost::parameter::void_ DefaultSetter;
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is)
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is)
typedef boost::parameter::parameters<
boost::parameter::optional<tag::Policy1_is>,
boost::parameter::optional<tag::Policy2_is>
> PolicySelector;
template
<
class PolicySetter1 = DefaultSetter,
class PolicySetter2 = DefaultSetter
>
class BreadSlicer
{
typedef typename PolicySelector::bind<
PolicySetter1,
PolicySetter2
>::type Policies;
public:
// extract policies:
typedef typename boost::parameter::value_type<
Policies, tag::Policy1_is, DefaultPolicy1
>::type P1;
typedef typename boost::parameter::value_type<
Policies, tag::Policy2_is, DefaultPolicy2
>::type P2;
};
The above code allows to override the optional template parameters of BreadSlicer
in arbritrary order by naming them Policy1_is
and Policy2_is
. This makes it very convenient to to policy-based design with many defaulted parameters.
int main()
{
typedef BreadSlicer<> B1;
// can override any default policy
typedef BreadSlicer< Policy1_is<int> > B2;
typedef BreadSlicer< Policy2_is<char> > B3;
// order of policy-setting is irrelevant
typedef BreadSlicer< Policy1_is<int>, Policy2_is<char> > B4;
typedef BreadSlicer< Policy2_is<char>, Policy1_is<int> > B5;
// similar static asserts work for B1 ... B4
BOOST_STATIC_ASSERT((std::is_same<B5::P1, int >::value));
BOOST_STATIC_ASSERT((std::is_same<B5::P2, char>::value));
return 0;
}
In order to avoid very subtle ODR violations with policy-based design (for an explanation see this old post by Alexandrescu), I would like to be able to apply a CRTP pattern on the named template arguments:
int main()
{
// ERROR: this code does NOT compile!
struct CuriousBreadSlicer
:
BreadSlicer< Policy1_is<CuriousBreadSlicer> >
{};
typedef CuriousBreadSlicer B6;
BOOST_STATIC_ASSERT((std::is_same<B6::P1, CuriousBreadSlicer>::value));
BOOST_STATIC_ASSERT((std::is_same<B6::P2, DefaultPolicy2 >::value));
return 0;
}
However, the Boost.Parameter implementation above fails to compile because some internal static_assert fails with a message like (VC10 SP1)
'main::CuriousBreadSlicer' : an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_base_of'
Question: can this static checking be turned off? Either through a macro or a template trick?
As to possible work-arounds:
Policy1_is
class. This solves the
compile time errors, but it loses the order independence of overriding.So it seems that I am what golf players call "in between clubs". Which solution would be best?
A minimal example with no CRTP:
#include <boost/parameter.hpp>
#include <boost/static_assert.hpp>
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is)
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is)
typedef boost::parameter::parameters<
boost::parameter::optional<tag::Policy1_is>,
boost::parameter::optional<tag::Policy2_is>
> PolicySelector;
struct foo {};
struct bar {};
struct baz;
typedef typename PolicySelector::bind<foo, baz>::type Policies;
boost::parameter::value_type<Policies, tag::Policy1_is, bar>::type x; // <- !!!
So boost::parameter::value_type
requires the policy selector to be based on complete types, which is not the case for your handwritten classes.
I'm not completely sure why a class would need itself as its own policy. If you need that, perhaps you could wrap an incomplete type in something complete:
struct CuriousBreadSlicer : BreadSlicer <
Policy1_is<CuriousBreadSlicer *> > // <- compiles
Or you could use your own wrap_incomplete_type<>
template, for clarity.
When you use the policy, you can check if it's wrapped, and unwrap it.