* Alf P. Steinbach:
Original problem: a pack of options that may contain a great many simple
numerical and bitset options (defined by external API), and that should
be (1) extensible for at least two levels, like class derivation, (2)
easy to define, (3) easily constructible specifying only those options
of interest, defaulting on the rest, (4) clean notation for accessing
options, with compile time checking, and (5) if possible as efficient as
possible access of options.
I extended the solution posted else-thread so that it now supports arbitrary
levels of extension (I've only tested with two levels, though, as shown below).
That amounted to removing two lines, and inserting two lines.
So, for those interested in trying it out, or commenting,
<code>
#include <boost/mpl/clear.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/pop_front.hpp>
namespace cppx { namespace typelist {
typedef boost::mpl::list<>::type Empty;
template< class TypeList >
struct InheritLinearly_
{
typedef typename boost::mpl::inherit_linearly<
TypeList,
boost::mpl::inherit said:
};
template< class TypeList >
struct Car_
{
typedef typename boost::mpl::front< TypeList >::type Type;
};
template< class TypeList >
struct Cdr_
{
typedef typename boost::mpl:
op_front< TypeList >::type Type;
};
#define CPPX_TYPELIST_ ::boost::mpl::list
} } // namespace cppx::typelist
namespace cppx { namespace options {
template< class T, class UniqueIdType >
class Value_
{
private:
T myValue;
public:
Value_(): myValue() {}
Value_( T const& v ): myValue( v ) {}
T const& value() const { return myValue; }
void setValue( T const& v ) { myValue = v; }
operator T const& () const { return value(); }
Value_& operator=( T const& v )
{
setValue( v ); return *this;
}
};
class NoBase {};
template< class SetterResult, class TopBase >
class NoBase_InheritTemplatedSetters_: public TopBase {};
template<
class OptionTypes,
class SetterResult,
class TopBase,
template said:
class InheritTemplatedSetters_;
template<
class SetterResult,
class TopBase,
template said:
class InheritTemplatedSetters_<
cppx::typelist::Empty, SetterResult, TopBase, OptionSetter_
: public TopBase
{};
template<
class OptionTypes,
class SetterResult,
class TopBase,
template said:
class InheritTemplatedSetters_
: public OptionSetter_<
typename cppx::typelist::Car_<OptionTypes>::Type,
SetterResult,
InheritTemplatedSetters_<
typename cppx::typelist::Cdr_<OptionTypes>::Type,
SetterResult,
TopBase,
OptionSetter_
{};
#define CPPX_OPTION( name ) OptionValue_##name
#define CPPX_DEFINE_OPTIONSETTER( name, type ) \
template< class OptionValue, class SetterResult, class Base > \
class OptionSetter_; \
\
template< class SetterResult, class Base > \
class OptionSetter_<CPPX_OPTION( name ), SetterResult, Base> \
: public Base \
{ \
public: \
type const& name() const \
{ return CPPX_OPTION( name )::value(); } \
\
SetterResult& name( type const& value ) \
{ \
CPPX_OPTION( name )::setValue( value ); \
return static_cast<SetterResult&>( *this ); \
} \
};
#define CPPX_DEFINE_OPTION_( name, type ) \
typedef ::cppx:
ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
CPPX_DEFINE_OPTIONSETTER( name, type )
#define CPPX_DEFINE_OPTIONV( name, type, defValue ) \
class CPPX_OPTION( name ) \
: public ::cppx:
ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
{ \
typedef ::cppx:
ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
public: \
CPPX_OPTION( name )(): Base( defValue ) {} \
CPPX_OPTION( name )( type const& v ): Base( v ) {} \
}; \
CPPX_DEFINE_OPTIONSETTER( name, type )
#define CPPX_DEFINE_VALUECLASS( name, base ) \
class name##_Values \
: public base \
, public ::cppx::typelist::InheritLinearly_< \
name##_OptionTypes \
{};
#define CPPX_DEFINE_TEMPLATED_SETTERS_INHERITANCE( name, base ) \
template< class SetterResult, class TopBase > \
class name##_InheritTemplatedSetters_ \
: public ::cppx:
ptions::InheritTemplatedSetters_< \
name##_OptionTypes, \
SetterResult, \
base##_InheritTemplatedSetters_< \
SetterResult, TopBase \
OptionSetter_ \
{};
#define CPPX_DEFINE_OPTIONCLASS( name, base ) \
class name \
: public name##_InheritTemplatedSetters_< \
name, \
name##_Values \
{};
#define CPPX_DEFINE_VTANDO( name, base ) \
CPPX_DEFINE_VALUECLASS( name, base ) \
CPPX_DEFINE_TEMPLATED_SETTERS_INHERITANCE( name, base ) \
CPPX_DEFINE_OPTIONCLASS( name, base )
#define CPPX_DEFINE_1OPTIONCLASS( name, base, option1 ) \
typedef CPPX_TYPELIST_< \
CPPX_OPTION( option1 ) \
CPPX_DEFINE_VTANDO( name, base )
#define CPPX_DEFINE_2OPTIONCLASS( name, base, option1, option2 ) \
typedef CPPX_TYPELIST_< \
CPPX_OPTION( option1 ), \
CPPX_OPTION( option2 ) \
CPPX_DEFINE_VTANDO( name, base )
} } // namespace cppx:
ptions
//------------------------ Usage:
#include <iostream>
namespace blahblah {
namespace reallyLongName {
namespace california {
struct desert
{
CPPX_DEFINE_OPTION_( x, double )
CPPX_DEFINE_OPTIONV( y, double, 12345 )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx:
ptions::NoBase, x, y )
};
}}} // namespace blahblah::reallyLongName::california
namespace blahblah {
namespace reallyLongName {
namespace texas {
struct rose
{
CPPX_DEFINE_OPTION_( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, california::desert:
os2Options, z )
};
}}} // namespace blahblah::reallyLongName::texas
namespace blahblah {
namespace reallyLongName {
namespace washington {
struct dc
{
CPPX_DEFINE_OPTIONV( a, double, 666 )
CPPX_DEFINE_1OPTIONCLASS( Pos4Options, texas::rose:
os3Options, a )
};
}}} // namespace blahblah::reallyLongName::washington
struct UserClass1
{
typedef ::blahblah::reallyLongName::california::desert:
os2Options
Pos2Options;
UserClass1( Pos2Options const& options )
{
using namespace std;
cout << options.x() << endl;
cout << options.y() << endl;
}
};
struct UserClass2: UserClass1
{
typedef ::blahblah::reallyLongName::texas::rose:
os3Options
Pos3Options;
UserClass2( Pos3Options const& options )
: UserClass1( options )
{
using namespace std;
cout << std::endl;
cout << options.x() << endl;
cout << options.y() << endl;
cout << options.z() << endl;
}
};
struct UserClass3: UserClass2
{
typedef ::blahblah::reallyLongName::washington::dc:
os4Options
Pos4Options;
UserClass3( Pos4Options const& options )
: UserClass2( options )
{
using namespace std;
cout << std::endl;
cout << options.x() << endl;
cout << options.y() << endl;
cout << options.z() << endl;
cout << options.a() << endl;
}
};
int main()
{
using namespace std;
typedef UserClass3:
os4Options Pos4Options;
UserClass3( Pos4Options().x( 3.14 ).z( 42 ) );
Pos4Options o;
cout << endl;
cout << typeid(o).name() << endl;
cout << "x: " << typeid(o.x(6)).name() << endl;
cout << "y: " << typeid(o.y(6)).name() << endl;
cout << "z: " << typeid(o.z(6)).name() << endl;
cout << "a: " << typeid(o.a(6)).name() << endl;
}
</code>
Cheers, & thanks to James for nudge in direction of using member routines,
- Alf
PS: Disclaimer: I haven't moved those changes back to the app I'm using this in.
So perhaps there are Issues. The only issue I know (I chose not to support that)
is that a base class can't be specified via any other name than the original.