Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

GUI options hierarchy revisited

32 views
Skip to first unread message

Alf P. Steinbach

unread,
May 18, 2018, 3:05:18 AM5/18/18
to
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:


----------------------------------------------------------------------------
#include <utility> // std::pair

namespace my
{
using std::pair;

template< class Value, class Key >
struct Option_value
{
Value value;

Option_value() = default;

Option_value( Key, Value v )
: value( v )
{}
};

template< class Value, class Key >
auto opt( Key, Value value )
-> Option_value<Value, Key>
{ return {Key{}, value}; }

template< class Options, class Key, class Value >
auto operator|( Options& o, Option_value<Value, Key> value )
-> Options&
{
static_cast<Option_value<Value, Key>&>( o ) = value;
return o;
}

template< class Options, class Key, class Value >
auto operator|( Options&& o, Option_value<Key, Value> value )
-> Options&
{ return o | value; }

namespace impl
{
template< class Key, class Value >
auto value_type( Option_value<Value, Key> const& ) -> Value;
}

template< class Options, class Key >
auto opt_item( Key, Options const& o )
{
using Value = decltype( impl::value_type<Key>( o ) );
return static_cast<Option_value<Value, Key> const&>( o ).value;
}

} // namespace my

namespace tag
{
using Position = struct Position_tag_struct*;
using Speed = struct Speed_tag_struct*;
} // namespace tag

struct Base_options
: my::Option_value<int, tag::Position>
{
Base_options() = default;
};

struct Derived_options
: Base_options
, my::Option_value<int, tag::Speed>
{
Derived_options() = default;
};

#include <iostream>
auto main()
-> int
{
using namespace std;
using namespace tag;
using my::opt;

auto const o = Derived_options{} | opt(Position{}, 1) |
opt(Speed{}, 2);
cout << "Position " << opt_item( Position{}, o ) << endl;
cout << "Speed " << opt_item( Speed{}, o ) << endl;
}
----------------------------------------------------------------------------


I haven't tried it out in any GUI code yet, but it seems like it could
do the job.

Problems? Existing solutions?


Cheers!

- Alf

Alf P. Steinbach

unread,
May 19, 2018, 7:31:21 PM5/19/18
to
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;

Melzzzzz

unread,
May 19, 2018, 8:03:22 PM5/19/18
to
On 2018-05-19, Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
> #include <iostream>
> auto main()
> -> int
What is advantage of writing auto and ->
instead of just int main()?

--
press any key to continue or any other to quit...

Ian Collins

unread,
May 19, 2018, 8:18:51 PM5/19/18
to
On 20/05/18 12:03, Melzzzzz wrote:
> On 2018-05-19, Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
>> #include <iostream>
>> auto main()
>> -> int
> What is advantage of writing auto and ->
> instead of just int main()?

None what so ever. Unless the intent is to antagonise readers...

--
Ian

Melzzzzz

unread,
May 19, 2018, 8:58:09 PM5/19/18
to
One question, though.

auto main() {
return 0;
}
Why does this does not works? Ordinary function it is fine, but main, no
go ;)

Alf P. Steinbach

unread,
May 20, 2018, 2:16:41 AM5/20/18
to
On 20.05.2018 02:03, Melzzzzz wrote:
> On 2018-05-19, Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
>> #include <iostream>
>> auto main()
>> -> int
> What is advantage of writing auto and ->
> instead of just int main()?

Using just a single syntax for function declarations.

There's no advantage in using two.


Cheers & hth.,

Alf

Christian Gollwitzer

unread,
May 20, 2018, 4:34:16 AM5/20/18
to
Am 20.05.18 um 01:31 schrieb Alf P. Steinbach:
>     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;

IMHO thus is still too verbose and complicated. Think of widgets which
can have 30 options. I'd like to see something like a JSON dictionary
syntax:

auto opt = { Position: 1, Speed: 2 };

Is there a way to do it from an initializer list? Maybe in pairs:

auto opt = { Position, 1, Speed, 2 };

Of course some "decoration" would be OK, like

auto opt = make_options({
Position, 1,
Speed, 2
})


(my C++17 skills are not strong enough to actually build this, just a
proposal from my experience with GUI in scripting languages)

Christian
0 new messages