On 18.05.2018 09:05, Alf P. Steinbach wrote:
> Ref my efforts for a GUI options hierarchy back in 2010: <url:
>
http://www.drdobbs.com/cpp/pure-c-options-classes/224700827>
>
> That solution was very complicated, using dirty template tricks.
>
> Now I'm thinking about something like the following:
> [snip originally posted code]
After sleeping on it + some work it's more acceptable:
#include <type_traits> // std::is_base_of
#include <utility> // std::(enable_if_t, move)
namespace cppx
{
using std::is_base_of;
// is_base_and_derived<B, D> == std::is_base_of_v<B, D>
template< class Base, class Derived >
constexpr bool is_base_and_derived = is_base_of<Base, Derived>::value;
}
// Preferably use this only in namespace `::tag`
#define CPPX_DEFINE_TAGTYPE( name ) \
using name = struct name ## _tag_struct volatile*
namespace cppx
{
inline namespace options
{
using std::enable_if_t;
using std::move;
struct Options_class_tag {};
template< class Value, class Key >
struct Option_value_
: virtual Options_class_tag
{
Value value;
Option_value_() = default;
Option_value_( Key, Value v )
: value( move( v ) )
{}
};
// Reads like `Opt{} | value_of<Blah>( 123 ) |
value_of<Gnorbl>( '@' )`.
template< class Key, class Value >
auto value_of( Value v )
-> Option_value_<Value, Key>
{ return {Key{}, move( v )}; }
template< class Options, class Key, class Value >
auto set_value_in( Options& o, Option_value_<Value, Key> value_of )
-> Options&
{
static_cast<Option_value_<Value, Key>&>( o ) = move(
value_of );
return o;
}
namespace impl
{
template< class Key, class Value >
auto value_type( Option_value_<Value, Key> const& ) -> Value;
}
template< class Options, class Key
, class = enable_if_t< is_base_and_derived<
Options_class_tag, Options > >
>
auto value_in( Options const& o, Key )
{
using Value = decltype( impl::value_type<Key>( o ) );
return static_cast<Option_value_<Value, Key> const&>( o
).value;
}
//------------------------------------------------------------------
// Syntactic sugar
template< class Options, class Key, class Value >
auto operator|( Options&& o, Option_value_<Value, Key> value_of )
-> Options&&
{ return move( set_value_in( o, value_of ) ); }
template< class Options, class Key, class Value >
auto operator|( Options& o, Option_value_<Key, Value> value_of )
-> Options
{
Options copy = o;
set_value_in( copy, value_of );
return copy;
}
template< class Options, class Key
, class = enable_if_t< is_base_and_derived<
Options_class_tag, Options > >
>
auto item( Key, Options const& o )
{ return value_in( o, Key{} ); }
} // namespace options
} // namespace cppx
namespace tag
{
CPPX_DEFINE_TAGTYPE( Position );
CPPX_DEFINE_TAGTYPE( Speed );
CPPX_DEFINE_TAGTYPE( Speed ); // Just to show that multiple
defs are OK.
} // namespace tag
struct Base_options
: cppx::Option_value_<int, tag::Position>
{};
struct Derived_options
: Base_options
, cppx::Option_value_<int, tag::Speed>
{};
#include <iostream>
auto main()
-> int
{
using namespace std;
using namespace tag;
using namespace cppx::options;
auto const o = Derived_options{} | value_of<Position>( 1 ) |
value_of<Speed>( 2 );
cout << "Position " << item( Position{}, o ) << endl;
cout << "Speed " << item( Speed{}, o ) << endl;