How can I use unqualified names? (Possibly hard or impossible?)


A

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.

My silly idea: represent option names as types, and support creation notation like

Options() << x(42) << alpha(3.14)

where 'x' and 'alpha' and other option names are type names. This allows compile
time access of options via notation like 'options::x.member' or e.g.
'options.member<x>', with efficiency as for ordinary member access. Defining a
set of options is reasonably simple, given a little template-based support.

Problem: long qualified option names are impractical and unreadable, but short
good names like 'x' can easily conflict with client code. Especially where that
client code cretating an Options instance is a constructor initializer list a
'using namespace...' isn't practical (as far as I can see, but I may be wrong).
I started thinking in terms of ADL and such, but no dice: my brain cannot come
up with good solution, even now after having slept on it!

Any help appreciated.

Test-of-concept-code, although perhaps best ignored since it constitutes the box
that I'm unable to break out of (I'm not using typelists cause this is just
testing, and also because that would possibly complicate usage):

<code>
template< typename OptionValues >
class Options_: public OptionValues
{
public:
template< typename OptionType >
Options_& operator<<( OptionType const& option )
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
static_cast<OptionType&>(*this) = option;
return *this;
}

template< typename OptionType >
OptionType const& as() const
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
return static_cast<OptionType const&>( *this );
}
};

template< typename T, typename Unique >
struct OptionsMember_
{
T member;
OptionsMember_(): member() {}
OptionsMember_( T const& aValue ): member( aValue ) {}
operator T& () { return member; }
operator T const& () const { return member; }
};

struct Nix {};

template<
class T,
class U = Nix,
class V = Nix,
class W = Nix,
class X = Nix,
class Y = Nix,
class Z = Nix
struct WithMembers_;

template< class T, class U, class V, class W, class X, class Y, class Z >
struct WithMembers_: T, U, V, W, X, Y, Z {};

template< class T, class U, class V, class W, class X, class Y >
struct WithMembers_<T, U, V, W, X, Y>: T, U, V, W, X, Y {};

template< class T, class U, class V, class W, class X >
struct WithMembers_<T, U, V, W, X>: T, U, V, W, X {};

template< class T, class U, class V, class W >
struct WithMembers_<T, U, V, W>: T, U, V, W {};

template< class T, class U, class V >
struct WithMembers_<T, U, V>: T, U, V {};

template< class T, class U >
struct WithMembers_<T, U>: T, U {};

template< class T >
struct WithMembers_<T>: T {};



//------------------------ Usage:

#include <iostream>

typedef OptionsMember_<double, struct UniqueTypeFor_x> x;
typedef OptionsMember_<double, struct UniqueTypeFor_y> y;
typedef Options_< WithMembers_<x, y> > XYOptions;

void foo( XYOptions const& options )
{
using namespace std;
cout << options.x::member << endl;
cout << options.y::member << endl;
}


typedef OptionsMember_<double, struct UniqueTypeFor_z> z;
typedef Options_< WithMembers_<XYOptions, z> > XYZOptions;

void foo( XYZOptions const& options )
{
using namespace std;
foo( options.as<XYOptions>() );
cout << std::endl;
cout << options.x::member << endl;
cout << options.y::member << endl;
cout << options.z::member << endl;
}


int main()
{
foo( XYZOptions() << x(3.14) << z(42) );
}
</code>


Cheers & TIA for any help,

- Alf
 
Ad

Advertisements

J

Jonathan Lee

My silly idea: represent option names as types, and support creation notation like

   Options() << x(42) << alpha(3.14)

Not sure how valuable my advice is, but here are my thoughts:

- Have your options derive from a base Options class
- Define operator& between two different Options-derived
classes to produce another Options-derived class that
combines the two into a single object
- Input the "and-ed" options to your destination object
via operator<< or similar

For example, suppose "foo" is to receive the options:

foo << (x(3.14) & z(42.0))

I would read this as "foo" should have x equal to 3.14
*and* z equal to 42.0 (*and* blah blah). I personally
like the & notation. Reminds me of combining bit flags
with | but it sounds better to say "and" than "or" :/

x would be something like Options<x> and z would be
Options<z> and the result of 'and-ing' them would be
Options<x,z>. Continue indefinitely so you build up
an object of type Options<x,z,w,t,v> or whatever. So
it should be extensible.

It also gets rid of the need for a dummy Options()
object to start the chain.

I don't know if this can be done; I'm no good at
templates. But that's my idea.

--Jonathan
 
J

Jonathan Lee

Not sure how valuable my advice is, but here are my thoughts:

Sorry, afterthought. Defining more and more Options
classes w/ templates might be possible if there is a
single-type version and double-type version.

i.e.
Options<x>
and Options<x,y>

then Options<x,y,z> would really be

Options<Options<x,y>, z>

and the number of cases for overloading << would be
small (well, 2).

--Jonathan
 
J

Jonathan Lee

So the following works, but it's kinda ugly. OK, it's really ugly. I
had to introduce a partial specialization of Options<type1, type2> to
get it to compile. I chose void* as type2; I don't really know how to
get around this. This means that I had to define a operator<<(void*)
on the test class Bar. And, basically there are void*'s scattered all
over. But the usage is as I described above.

I also defined trivial classes to wrap double (called xbase, zbase)
and int (called ybase). Maybe a typedef is sufficient?

// code -----------------------------------------

#include <iostream>

class xbase {
public:
double x;
xbase(double y) : x(y) {}
};

class ybase {
public:
int y;
ybase(int x) : y(x) {}
};

class zbase {
public:
double z;
zbase(double y) : z(y) {}
};

template<typename T, typename U>
class Options {
public:
T val;
U other;
Options(T t, U u) : val(t), other(u) { }
};

template<typename T>
class Options<T, void*> {
public:
T val;
void* other;
Options(T x) : val(x) {}
};

template<typename T, typename U, typename V>
Options<Options<T, U>, V> operator&(Options<T, U> t, Options<V, void*>
u) {
return Options<Options<T, U>, V>(t,u.val); }

class Bar {
xbase x1;
zbase z1;
ybase y1;
public:
Bar() : x1(0.0), z1(0.0), y1(0) { }
void print() {
std::cout << x1.x << std::endl;
std::cout << y1.y << std::endl;
std::cout << z1.z << std::endl;
}

Bar& operator<<(zbase zz) {
z1 = zz;
return (*this);
}

Bar& operator<<(xbase xx) {
x1 = xx;
return (*this);
}

Bar& operator<<(ybase yy) {
y1 = yy;
return (*this);
}

Bar& operator<<(void*) {
return (*this);
}

template<typename T, typename U>
Bar& operator<<(Options<T, U> otu) {
operator<<(otu.val);
operator<<(otu.other);
return (*this);
}
};

typedef Options<xbase, void*> xtype;
typedef Options<zbase, void*> ztype;
typedef Options<ybase, void*> ytype;

int main() {
Bar foo;
foo << (xtype(1.0) & ztype(2.0) & ytype(18));
foo.print();
return 0;
}
 
A

Alf P. Steinbach

* Alf P. Steinbach:
Problem: long qualified option names are impractical and unreadable, but
short good names like 'x' can easily conflict with client code.
Especially where that client code cretating an Options instance is a
constructor initializer list a 'using namespace...' isn't practical (as
far as I can see, but I may be wrong). I started thinking in terms of
ADL and such, but no dice: my brain cannot come up with good solution,
even now after having slept on it!

First, thanks to Jonathan Lee (else-thread) for a different tack on the option
pack problem, the problem that led to the above. Yeah, I'm using that and'ing
scheme for simpler options, those that are encoded as bit-fields. But I don't
quite see how it helps with the above.

Anyway, I found a sort of almost-solution, shown below.

It doesn't get rid of qualification as I wanted, but it reduces qualification to
only one qualifier, which can be freely named within a usage class.

It's sort of like if one could have 'using namespace foo=' within a class. :)

Might be a useful technique on its own.


<code>
template< typename OptionValues >
class Options_: public OptionValues
{
public:
template< typename OptionType >
Options_& operator<<( OptionType const& option )
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
static_cast<OptionType&>(*this) = option;
return *this;
}

template< typename OptionType >
OptionType const& as() const
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
return static_cast<OptionType const&>( *this );
}
};

template< typename T, typename Unique >
struct OptionsMember_
{
T member;
OptionsMember_(): member() {}
OptionsMember_( T const& aValue ): member( aValue ) {}
operator T& () { return member; }
operator T const& () const { return member; }
};

struct Nix {};

template<
class T,
class U = Nix,
class V = Nix,
class W = Nix,
class X = Nix,
class Y = Nix,
class Z = Nix
struct WithMembers_;

template< class T, class U, class V, class W, class X, class Y, class Z >
struct WithMembers_: T, U, V, W, X, Y, Z {};

template< class T, class U, class V, class W, class X, class Y >
struct WithMembers_<T, U, V, W, X, Y>: T, U, V, W, X, Y {};

template< class T, class U, class V, class W, class X >
struct WithMembers_<T, U, V, W, X>: T, U, V, W, X {};

template< class T, class U, class V, class W >
struct WithMembers_<T, U, V, W>: T, U, V, W {};

template< class T, class U, class V >
struct WithMembers_<T, U, V>: T, U, V {};

template< class T, class U >
struct WithMembers_<T, U>: T, U {};

template< class T >
struct WithMembers_<T>: T {};



//------------------------ Usage:

#include <iostream>

namespace blahblah {
namespace reallyLongName {
namespace california {
namespace pos2 {
typedef OptionsMember_<double, struct UniqueTypeFor_x> x;
typedef OptionsMember_<double, struct UniqueTypeFor_y> y;
typedef Options_< WithMembers_<x, y> > Options;

struct Names
{
typedef pos2::Options Options;
typedef pos2::x x;
typedef pos2::y y;
};
}}}} // namespace blahblah::reallyLongName::california::pos2


struct UserClass1
{
//using namespace pos2 = blahblah::reallyLongName::california::pos2;
typedef ::blahblah::reallyLongName::california::pos2::Names pos2;

UserClass1( pos2::Options const& options )
{
using namespace std;
cout << options.pos2::x::member << endl;
cout << options.pos2::y::member << endl;
}
};


namespace blahblah {
namespace reallyLongName {
namespace california {
namespace pos3 {
typedef OptionsMember_<double, struct UniqueTypeFor_z> z;
typedef Options_< WithMembers_<pos2::Options, z> > Options;

struct Names: pos2::Names
{
typedef pos3::Options Options;
typedef pos3::z z;
};
}}}} // namespace blahblah::reallyLongName::california::pos3


struct UserClass2: UserClass1
{
//using namespace pos3 = blahblah::reallyLongName::california::pos3;
typedef ::blahblah::reallyLongName::california::pos3::Names pos3;

UserClass2( pos3::Options const& options )
: UserClass1( options )
{
using namespace std;
cout << std::endl;
cout << options.pos3::x::member << endl;
cout << options.pos3::y::member << endl;
cout << options.pos3::z::member << endl;
}
};

int main()
{
//using namespace pos3 = blahblah::reallyLongName::california::pos3;
typedef UserClass2::pos3 pos3;
UserClass2( pos3::Options() << pos3::x(3.14) << pos3::z(42) );
}
</code>


Now, if I could just see some way to use e.g. macros to make the option pack
definitions less verbose & more readable...


Cheers,

- Alf
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Alf P. Steinbach:

First, thanks to Jonathan Lee (else-thread) for a different tack on the
option pack problem, the problem that led to the above. Yeah, I'm using
that and'ing scheme for simpler options, those that are encoded as
bit-fields. But I don't quite see how it helps with the above.

Anyway, I found a sort of almost-solution, shown below.

It doesn't get rid of qualification as I wanted, but it reduces
qualification to only one qualifier, which can be freely named within a
usage class.

It's sort of like if one could have 'using namespace foo=' within a
class. :)

Might be a useful technique on its own.

Uh oh, I should have tested with MSVC.

MSVC 7.1 doesn't accept the 'typedef'. But it does accept a class derivation.
However, results are incorrect relative to what I expect: I expect the output
that compilation with g++ produces.

Results g++ versus MSVC are posted below after the new adjusted-for-MSVC code.


<code>
template< typename OptionValues >
class Options_: public OptionValues
{
public:
template< typename OptionType >
Options_& operator<<( OptionType const& option )
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
static_cast<OptionType&>(*this) = option;
return *this;
}

template< typename OptionType >
OptionType const& as() const
{
//STATIC_ASSERT( IS_DERIVED_AND_BASE( Options_, OptionType ) );
return static_cast<OptionType const&>( *this );
}
};

template< typename T, typename Unique >
struct OptionsMember_
{
T member;
OptionsMember_(): member() {}
OptionsMember_( T const& aValue ): member( aValue ) {}
operator T& () { return member; }
operator T const& () const { return member; }
};

struct Nix {};

template<
class T,
class U = Nix,
class V = Nix,
class W = Nix,
class X = Nix,
class Y = Nix,
class Z = Nix
struct WithMembers_;

template< class T, class U, class V, class W, class X, class Y, class Z >
struct WithMembers_: T, U, V, W, X, Y, Z {};

template< class T, class U, class V, class W, class X, class Y >
struct WithMembers_<T, U, V, W, X, Y>: T, U, V, W, X, Y {};

template< class T, class U, class V, class W, class X >
struct WithMembers_<T, U, V, W, X>: T, U, V, W, X {};

template< class T, class U, class V, class W >
struct WithMembers_<T, U, V, W>: T, U, V, W {};

template< class T, class U, class V >
struct WithMembers_<T, U, V>: T, U, V {};

template< class T, class U >
struct WithMembers_<T, U>: T, U {};

template< class T >
struct WithMembers_<T>: T {};



//------------------------ Usage:

#include <iostream>

namespace blahblah {
namespace reallyLongName {
namespace california {
namespace pos2 {
typedef OptionsMember_<double, struct UniqueTypeFor_x> x;
typedef OptionsMember_<double, struct UniqueTypeFor_y> y;
typedef Options_< WithMembers_<x, y> > Options;

struct Names
{
typedef pos2::Options Options;
typedef pos2::x x;
typedef pos2::y y;
};
}}}} // namespace blahblah::reallyLongName::california::pos2


struct UserClass1
{
//using namespace pos2 = blahblah::reallyLongName::california::pos2;
struct pos2: ::blahblah::reallyLongName::california::pos2::Names {};

UserClass1( pos2::Options const& options )
{
using namespace std;
cout << options.pos2::x::member << endl;
cout << options.pos2::y::member << endl;
}
};


namespace blahblah {
namespace reallyLongName {
namespace california {
namespace pos3 {
typedef OptionsMember_<double, struct UniqueTypeFor_z> z;
typedef Options_< WithMembers_<pos2::Options, z> > Options;

struct Names: pos2::Names
{
typedef pos3::Options Options;
typedef pos3::z z;
};
}}}} // namespace blahblah::reallyLongName::california::pos3


struct UserClass2: UserClass1
{
//using namespace pos3 = blahblah::reallyLongName::california::pos3;
struct pos3: ::blahblah::reallyLongName::california::pos3::Names {};

UserClass2( pos3::Options const& options )
: UserClass1( options )
{
using namespace std;
cout << std::endl;
cout << options.pos3::x::member << endl;
cout << options.pos3::y::member << endl;
cout << options.pos3::z::member << endl;
}
};

int main()
{
//using namespace pos3 = blahblah::reallyLongName::california::pos3;
typedef UserClass2::pos3 pos3;
UserClass2( pos3::Options() << pos3::x(3.14) << pos3::z(42) );
}
</code>


<example>
C:\temp> gnuc x.cpp -o a && a
3.14
0

3.14
0
42

C:\temp> msvc x.cpp -o b && b
x.cpp
3.14
3.14

3.14
3.14
3.14

C:\temp>
</example>


Does this mean that the code above has UB?

And if not, can one conclude that MSVC 7.1 is wrong?


Cheers & TIA.,

- Alf
 
Ad

Advertisements

J

James Kanze

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.

Sounds like something in a GUI:).
My silly idea: represent option names as types, and support
creation notation like
Options() << x(42) << alpha(3.14)
where 'x' and 'alpha' and other option names are type names.
This allows compile time access of options via notation like
'options::x.member' or e.g. 'options.member<x>', with
efficiency as for ordinary member access. Defining a set of
options is reasonably simple, given a little template-based
support.

Not silly at all. It's very similar to the "standard" solution,
which would also use an Options class, but with member functions
which chain (i.e. return an Options&):

Options().x( 42 ).alpha( 3.14 ) ...

I usually provide a copy constructor for Options as well, so
that the client can set up special defaults for specific cases,
e.g.:

Options( warningPopup ).x( 42 )

(where warningPopup is a static variable of type Options, with
some different set of options than the usual default).
Problem: long qualified option names are impractical and
unreadable, but short good names like 'x' can easily conflict
with client code. Especially where that client code cretating
an Options instance is a constructor initializer list a 'using
namespace...' isn't practical (as far as I can see, but I may
be wrong). I started thinking in terms of ADL and such, but
no dice: my brain cannot come up with good solution, even now
after having slept on it!

The member function solution works for me. The one disadvantage
I can see is that you have to type out all of the member
functions; I'll usually use a simple AWK script to generate the
class, so I don't even have to do this. (The script will
convert a file containing lines something like:
optionName optionType defaultValue
into a complete class definition, with all of the necessary
access functions and the member variables.)
 
A

Alf P. Steinbach

* James Kanze:
Sounds like something in a GUI:).

Right on. :)

I let it simmer for a time, actually I think it's been a week now, hoping for
fresh ideas.

But in the end I now just combined your member routines suggestion with my
previous idea of options as types.

Not silly at all. It's very similar to the "standard" solution,
which would also use an Options class, but with member functions
which chain (i.e. return an Options&):

Options().x( 42 ).alpha( 3.14 ) ...

I usually provide a copy constructor for Options as well, so
that the client can set up special defaults for specific cases,
e.g.:

Options( warningPopup ).x( 42 )

(where warningPopup is a static variable of type Options, with
some different set of options than the usual default).


The member function solution works for me. The one disadvantage
I can see is that you have to type out all of the member
functions; I'll usually use a simple AWK script to generate the
class, so I don't even have to do this.

Uh huh? Why not macros & templates & stuff? Pure C++ solution?

(The script will
convert a file containing lines something like:
optionName optionType defaultValue
into a complete class definition, with all of the necessary
access functions and the member variables.)

My test usage code for all-C++ solution now looks like this:


<code>
//------------------------ Usage:

#include <iostream>

namespace blahblah {
namespace reallyLongName {
namespace california {
CPPX_DEFINE_OPTION( x, double )
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )

CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )
}}} // namespace blahblah::reallyLongName::california

struct UserClass1
{
typedef ::blahblah::reallyLongName::california::pos2Options
Pos2Options;

UserClass1( Pos2Options const& options )
{
using namespace std;
cout << options.x() << endl;
cout << options.y() << endl;
}
};

struct UserClass2: UserClass1
{
typedef ::blahblah::reallyLongName::california::pos3Options
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;
}
};

int main()
{
typedef UserClass2::pos3Options Pos3Options;
UserClass2( Pos3Options().x( 3.14 ).z( 42 ) );
}
</code>


<results>
3.14
0

3.14
0
42
</results>


Works with both g++ and MSVC.

I'm interested in whether anyone can find a /simple/ way to do it, because the
support needed to get that final

Pos3Options().x( 3.14 ).z( 42 )

expression to work turned out, with the way I did it, to be a little in-elegant
(I guess that's what some folks call "too smart" code)...


Cheers, & thanks,

- Alf
 
J

James Kanze

* James Kanze:
Right on. :)
I let it simmer for a time, actually I think it's been a week
now, hoping for fresh ideas.

I've found that often to be a good idea as well.
But in the end I now just combined your member routines
suggestion with my previous idea of options as types.
Uh huh? Why not macros & templates & stuff? Pure C++ solution?

I didn't consider templates, perhaps because when I did it, C++
didn't have templates, but I don't really see where they would
help; since my attributes don't have different types, I don't
quite see how you could trigger the correct template use. You
certainly could do it just as well with macros, but I found an
external code generator simpler and cleaner (if nothing else,
for the default values).
My test usage code for all-C++ solution now looks like this:
<code>
//------------------------ Usage:
#include <iostream>
namespace blahblah {
namespace reallyLongName {
namespace california {
CPPX_DEFINE_OPTION( x, double )

I suppose that this is your macro, but I'm not sure what it's
supposed to generate. In a macro based solution, I would have
expected to see something like:

#define MYPREFIX_DEFINE_OPTION( name, type )\
public: \
Options& name( type new_ ## name ) \
{ \
my_ ## name = new_ ## name ; \
return *this ; \
} \
type name() const \
{ \
return my_ ## name ; \
} \
private: \
type my_ ## name

class Options
{
MYPREFIX_DEFINE_OPTION( y, double ) ;
// ...
} ;

#undef MYPREFIX_DEFINE_OPTION

That still leaves having to list all of the defaults in the
constructor. (The AWK script would only be a few lines longer
than the above, and would also generate the constructor.)
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )
CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )
}}} // namespace blahblah::reallyLongName::california
struct UserClass1
{
typedef ::blahblah::reallyLongName::california::pos2Options
Pos2Options;
UserClass1( Pos2Options const& options )
{
using namespace std;
cout << options.x() << endl;
cout << options.y() << endl;
}
};
struct UserClass2: UserClass1
{
typedef ::blahblah::reallyLongName::california::pos3Options
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;
}
};
int main()
{
typedef UserClass2::pos3Options Pos3Options;
UserClass2( Pos3Options().x( 3.14 ).z( 42 ) );
}
</code>


Works with both g++ and MSVC.
I'm interested in whether anyone can find a /simple/ way to do
it, because the support needed to get that final
Pos3Options().x( 3.14 ).z( 42 )
expression to work turned out, with the way I did it, to be a
little in-elegant (I guess that's what some folks call "too
smart" code)...

I don't know. I don't really understand what's going on,
without seeing the definitions of the macros you're using.
 
A

Alf P. Steinbach

* James Kanze:
* James Kanze:

[snip]
My test usage code for all-C++ solution now looks like this:
<code>
//------------------------ Usage:
#include <iostream>
namespace blahblah {
namespace reallyLongName {
namespace california {
CPPX_DEFINE_OPTION( x, double )

I suppose that this is your macro, but I'm not sure what it's
supposed to generate. In a macro based solution, I would have
expected to see something like:

#define MYPREFIX_DEFINE_OPTION( name, type )\
public: \
Options& name( type new_ ## name ) \
{ \
my_ ## name = new_ ## name ; \
return *this ; \
} \
type name() const \
{ \
return my_ ## name ; \
} \
private: \
type my_ ## name

class Options
{
MYPREFIX_DEFINE_OPTION( y, double ) ;
// ...
} ;

#undef MYPREFIX_DEFINE_OPTION

That still leaves having to list all of the defaults in the
constructor. (The AWK script would only be a few lines longer
than the above, and would also generate the constructor.)

Well, one beauty of having each option as a type is that each option has its own
constructor, so that the default can be specified along with the "declaration"
of the option, like

CPPX_DEFINE_OPTION_AND_DEFAULT( y, double, 12345 )

I don't know. I don't really understand what's going on,
without seeing the definitions of the macros you're using.

They're ugly! :)

Or "in-elegant"...

Hence the plea for simpler pure-C++ solution that supports the same, in
particular extension and simple notation (client code).


<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::clear< boost::mpl::list<> >::type Empty;

template< class TypeList >
struct InheritLinearly_
{
typedef typename boost::mpl::inherit_linearly<
TypeList,
boost::mpl::inherit said:
>::type Type;
};

template< class TypeList >
struct Car
{
typedef typename boost::mpl::front< TypeList >::type Type;
};

template< class TypeList >
struct Cdr
{
typedef typename boost::mpl::pop_front< TypeList >::type Type;
};

} } // 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_TemplatedSetters_: public TopBase {};

template<
class OptionTypes,
class SetterResult,
class TopBase,
template said:
class TemplatedSetters_;

template<
class SetterResult,
class TopBase,
template said:
class TemplatedSetters_<
cppx::typelist::Empty, SetterResult, TopBase, OptionSetter_
: public TopBase
{};

template<
class OptionTypes,
class SetterResult,
class TopBase,
template said:
class TemplatedSetters_
: public OptionSetter_<
typename cppx::typelist::Car<OptionTypes>::Type,
SetterResult,
TemplatedSetters_<
typename cppx::typelist::Cdr<OptionTypes>::Type,
SetterResult,
TopBase,
OptionSetter_
{};

#define CPPX_OPTION( name ) OptionValue_##name

#define CPPX_DEFINE_OPTION_SETTER( 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::eek:ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
> CPPX_OPTION( name ); \
CPPX_DEFINE_OPTION_SETTER( name, type )

#define CPPX_DEFINE_OPTION_AND_DEFAULT( name, type, defValue ) \
class CPPX_OPTION( name ) \
: public ::cppx::eek:ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
{ \
typedef ::cppx::eek:ptions::Value_< \
type, struct UniqueIdTypeFor_##name \
> Base; \
public: \
CPPX_OPTION( name )(): Base( defValue ) {} \
CPPX_OPTION( name )( type const& v ): Base( v ) {} \
}; \
CPPX_DEFINE_OPTION_SETTER( name, type )

#define CPPX_DEFINE_VALUECLASS( name, base ) \
class name##_Values \
: public base \
, public ::cppx::typelist::InheritLinearly_< \
name##_OptionTypes \
>::Type \
{};

#define CPPX_DEFINE_TEMPLATED_SETTERS( name ) \
template< class SetterResult, class TopBase > \
class name##_TemplatedSetters_ \
: public ::cppx::eek:ptions::TemplatedSetters_< \
name##_OptionTypes, \
SetterResult, \
TopBase, \
OptionSetter_ \
{};

#define CPPX_DEFINE_OPTIONCLASS( name, base ) \
class name \
: public name##_TemplatedSetters_< \
name, \
base##_TemplatedSetters_ said:
{};

#define CPPX_DEFINE_VTANDO( name, base ) \
CPPX_DEFINE_VALUECLASS( name, base ) \
CPPX_DEFINE_TEMPLATED_SETTERS( name ) \
CPPX_DEFINE_OPTIONCLASS( name, base )

#define CPPX_DEFINE_1OPTIONCLASS( name, base, option1 ) \
typedef ::boost::mpl::list< \
CPPX_OPTION( option1 ) \
> name##_OptionTypes; \
CPPX_DEFINE_VTANDO( name, base )

#define CPPX_DEFINE_2OPTIONCLASS( name, base, option1, option2 ) \
typedef ::boost::mpl::list< \
CPPX_OPTION( option1 ), \
CPPX_OPTION( option2 ) \
> name##_OptionTypes; \
CPPX_DEFINE_VTANDO( name, base )

// ...
} } // namespace cppx::eek:ptions
</code>


Cheers,

- Alf (he he, I *mean* it when I say something's "ugly" :) )
(but then, the ugly beast is easy to ride, good tempered animal)
 
J

James Kanze

* James Kanze:
They're ugly! :)
Or "in-elegant"...
Hence the plea for simpler pure-C++ solution that supports the
same, in particular extension and simple notation (client
code).

Just curious, but why does it have to be "pure-C++"? As I said
earlier, a short AWK script does the job just fine.

BTW: What is the Windows equivalent of AWK? There must be one,
or do all Windows programmers have CygWin or something similar
installed? Or Perl---you could easily do it in Perl, if you
can stand Perl? Or probably Python or Ruby, or just about any
"small language" that's text oriented. (All of the Windows
programmers I know do use CygWin, so maybe there is something
essential for program development missing in Windows.)

Anyway...
<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::clear< boost::mpl::list<> >::type Empty;

You've already lost me. I don't have the slightest knowledge of
Boost::mpl, and in general, I find most of the advanced
meta-programming idioms too complicated for general use.

At any rate, you've something like 200 lines of very complicated
code, to do something that can be done much simpler outside of
the language, or even with macros. Whereas something like:

#! /usr/bin/awk -f
/ *#/ { next }

NF == 3 || (NF > 3 && $4 ~ /^#/) {
++ optionCount
names[ optionCount ] = $1
types[ optionCount ] = $2
defaults[ optionCount ] = $3
next
}

{
printf( "[%s:%d]: syntax error\n", FILENAME, FNR ) > "/dev/
stderr"
}

END {
printf( "class Options\n" )
printf( "{\n" )
printf( "public:\n" )
printf( " Options::Options()\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %c %s( %s )\n", i == 1 ? ":" : "," ,names
[ i ], defaults[ i ] )
}
printf( " {\n" )
printf( " }\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
name = names[ i ]
type = types[ i ]
printf( " %s %s() const\n", type, name )
printf( " {\n" )
printf( " return my_%s;\n", name )
printf( " }\n" )
printf( " Options& %s( %s new_%s )\n", name, type,
name )
printf( " {\n" )
printf( " my_%s = new_%s ;\n", name, name )
printf( " return *this ;\n" )
printf( " }\n" )
}
printf( "private:\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %s my_%s ;\n", types[ i ], names[ i ] )
}
printf( "} ;\n" )
}

, invoked with "genOptions.awk -v className=Options
options.lst", is less than 50 lines (and would be less if I'd
compromize on the formatting of the generated class), and is a
lot simpler. (For one time use, this could be made even
simpler, by dropping support for comments, error checking, etc.
Or with very little effort and additional code, it could be made
more generic, supporting more than one specification in the same
file, with the class name specified in the file, and generating
two files, a header and source, for each class.

For that matter, if you're willing to use macros, what does your
solution buy you over:

#define MYPREFIX_DECLARE_ATTR( class ,name, type,
defaultValue ) \

private: \
Fallible< type > my_ ##
name ; \

public: \
type name()
const \

{ \
return my ## name.elseDefaultTo
( defaultValue ) ; \
}
\
class& name( type new_ ##
name ) \

{ \
my ## name.validate( new_ ##
name ) ; \
return
*this ; \
}

class Options
{
#define MYPREFIX_OPTION( name, type, defaultValue ) \
MYPREFIX_DECLARE_ATTR( Options, name, type, defaultValue )
MYPREFIX_OPTION( x, double, 3.14159 ) ;
MYPREFIX_OPTION( y, int, 42 ) ;
// ...
#undef MYPREFIX_OPTION
} ;

? The resulting class will be bigger than necessary (since each
Fallible requires an additional bool), and slightly slower, at
least if the types involved are cheap to construct, but I doubt
that it really matters in most cases.
 
Ad

Advertisements

A

Alf P. Steinbach

* James Kanze:
Just curious, but why does it have to be "pure-C++"? As I said
earlier, a short AWK script does the job just fine.

Why not pure C++?

If something as simple as this can't be expressed easily, then at least we can
(perhaps, hopefully) find out where the flaws in the language are.

BTW: What is the Windows equivalent of AWK?
VBScript.


There must be one,
or do all Windows programmers have CygWin or something similar
installed?

Yes. Plus freestanding *nix tools for Windows. And there must be some who run
Microsoft Services for Unix (SFU), which gives you *nix in Windows, with g++
redistributed by Microsoft...

Or Perl---you could easily do it in Perl, if you
can stand Perl? Or probably Python or Ruby, or just about any
"small language" that's text oriented. (All of the Windows
programmers I know do use CygWin, so maybe there is something
essential for program development missing in Windows.)

Not so much that Windows is missing stuff (it is, e.g. backquoting is horrible)
so much that e.g. most build scripts for libraries depend on *nix tools.

Anyway...




You've already lost me. I don't have the slightest knowledge of
Boost::mpl, and in general, I find most of the advanced
meta-programming idioms too complicated for general use.

I don't have the slightest knowledge either. I just used the MPL typelist,
checking the docs as I wrote, instead of locating or downloading new version of
Loki, or writing one from scratch (it's pretty simple, really). The above
expression is probably overkill for its purpose: it addresses that the type of
'mpl::list<>', a logically empty list of types, is not the same as the type of
an empty list produced by reduction of a list. Probably just

typedef boost::mpl::list<>::type Empty;

would suffice to define the "real" empty list type.

But I don't know, I've never used that typelist implementation before, and was a
bit surprised that this was necessary.

At any rate, you've something like 200 lines of very complicated
code, to do something that can be done much simpler outside of
the language, or even with macros. Whereas something like:

#! /usr/bin/awk -f
/ *#/ { next }

NF == 3 || (NF > 3 && $4 ~ /^#/) {
++ optionCount
names[ optionCount ] = $1
types[ optionCount ] = $2
defaults[ optionCount ] = $3
next
}

{
printf( "[%s:%d]: syntax error\n", FILENAME, FNR ) > "/dev/
stderr"
}

END {
printf( "class Options\n" )
printf( "{\n" )
printf( "public:\n" )
printf( " Options::Options()\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %c %s( %s )\n", i == 1 ? ":" : "," ,names
[ i ], defaults[ i ] )
}
printf( " {\n" )
printf( " }\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
name = names[ i ]
type = types[ i ]
printf( " %s %s() const\n", type, name )
printf( " {\n" )
printf( " return my_%s;\n", name )
printf( " }\n" )
printf( " Options& %s( %s new_%s )\n", name, type,
name )
printf( " {\n" )
printf( " my_%s = new_%s ;\n", name, name )
printf( " return *this ;\n" )
printf( " }\n" )
}
printf( "private:\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %s my_%s ;\n", types[ i ], names[ i ] )
}
printf( "} ;\n" )
}

, invoked with "genOptions.awk -v className=Options
options.lst", is less than 50 lines (and would be less if I'd
compromize on the formatting of the generated class),

Well 150 lines of code isn't much to talk about, really. :)

and is a lot simpler.

Only where it doesn't matter: for producing that support code in the first place.

For using it I think an all C++ solution such as my Friday code is much simpler,
both with respect to using it from scratch, with respect to maintainance (no
redundancy), and with respect to build configuration.

(For one time use, this could be made even
simpler, by dropping support for comments, error checking, etc.
Or with very little effort and additional code, it could be made
more generic, supporting more than one specification in the same
file, with the class name specified in the file, and generating
two files, a header and source, for each class.

For that matter, if you're willing to use macros, what does your
solution buy you over:

#define MYPREFIX_DECLARE_ATTR( class ,name, type,
defaultValue ) \

private: \
Fallible< type > my_ ##
name ; \

public: \
type name()
const \

{ \
return my ## name.elseDefaultTo
( defaultValue ) ; \
}
\
class& name( type new_ ##
name ) \

{ \
my ## name.validate( new_ ##
name ) ; \
return
*this ; \
}

class Options
{
#define MYPREFIX_OPTION( name, type, defaultValue ) \
MYPREFIX_DECLARE_ATTR( Options, name, type, defaultValue )
MYPREFIX_OPTION( x, double, 3.14159 ) ;
MYPREFIX_OPTION( y, int, 42 ) ;
// ...
#undef MYPREFIX_OPTION
} ;

?

This does not produce an extensible class.

In particular it can not, AFAICS, be used to produce the two option classes in
my example usage code shown earlier in thread (natural modifications OK, of
course), which you'll see if you try.

But you make me wonder: what does the script buy you over the above, which
AFAICS offers the same functionality with less code and easier maintainance?

The resulting class will be bigger than necessary (since each
Fallible requires an additional bool), and slightly slower, at
least if the types involved are cheap to construct, but I doubt
that it really matters in most cases.

If that's all that you require of the class then that scheme is superb, because
it's simple. :) But the little inefficiency is not necessary. Just replace
Fallible with a class that constructs the contained value with the specified
default. Of course you need one such class per option, even when the options are
of the same type. And then you have the basic options-as-types scheme... ;-)

But the above is not a superb solution to my little problem, for it does not let
you extend the resulting class except by requiring the code that uses the
setters to always use base class setters last, which to me feels very wrong.

I think that problem indicates that there's a missing feature in C++, because it
"should" be trivial to express, and indeed is trivial in any scripting language.


Cheers, & thanks,

- Alf
 
J

Jerry Coffin

(e-mail address removed)>, (e-mail address removed)
says...

[ ... ]
BTW: What is the Windows equivalent of AWK? There must be one,
or do all Windows programmers have CygWin or something similar
installed?

Nah -- various versions of AWK have been compiled natively for
Windows (and even MS-DOS) for years. One project that has ported a
reasonable number of the major GNU utilities to Windows (native, not
Cygwin) is at:

http://unxutils.sourceforge.net/

This includes both gawk and m4, either of which would be quite
adequate for jobs like this.
 
J

James Kanze

* James Kanze:
Why not pure C++?

Because some other tool does the job better? "Pure C++" isn't a
particular goal in my mind, any more than "pure OO" or pure
anything else. I use the tools best fitted for the job.
If something as simple as this can't be expressed easily, then
at least we can (perhaps, hopefully) find out where the flaws
in the language are.

Why is it necessarily a flaw in the language? No tool can be
perfect for everything. Use the most appropriate tool.
Yes. Plus freestanding *nix tools for Windows. And there must
be some who run Microsoft Services for Unix (SFU), which gives
you *nix in Windows, with g++ redistributed by Microsoft...

OK. (I'm starting a new job in September, in a purely Windows
environment. It will be the first time in my life I've been in
a purely Windows environment.)
Not so much that Windows is missing stuff (it is, e.g.
backquoting is horrible) so much that e.g. most build scripts
for libraries depend on *nix tools.

In a way, I'm happy to hear that. It means that I already know
a significant (and for me important) part of the environment
where I'm going:).
I don't have the slightest knowledge either. I just used the
MPL typelist, checking the docs as I wrote, instead of
locating or downloading new version of Loki, or writing one
from scratch (it's pretty simple, really). The above
expression is probably overkill for its purpose: it addresses
that the type of 'mpl::list<>', a logically empty list of
types, is not the same as the type of an empty list produced
by reduction of a list. Probably just
typedef boost::mpl::list<>::type Empty;
would suffice to define the "real" empty list type.
But I don't know, I've never used that typelist implementation
before, and was a bit surprised that this was necessary.

Well, that sounds like another reason to use something else. I
wouldn't use a tool that I didn't at least vaguely understand.
(It's a difficult issue, of course---I suspect that most C++
programmers wouldn't be capable of writing a C++ compiler, and
they shouldn't need that knowledge. But I don't even know what
Boost::mpl---or anything in Loki---really does.)
At any rate, you've something like 200 lines of very
complicated code, to do something that can be done much
simpler outside of the language, or even with macros.
Whereas something like:
#! /usr/bin/awk -f
/ *#/ { next }
NF == 3 || (NF > 3 && $4 ~ /^#/) {
++ optionCount
names[ optionCount ] = $1
types[ optionCount ] = $2
defaults[ optionCount ] = $3
next
}
{
printf( "[%s:%d]: syntax error\n", FILENAME, FNR ) > "/dev/
stderr"
}
END {
printf( "class Options\n" )
printf( "{\n" )
printf( "public:\n" )
printf( " Options::Options()\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %c %s( %s )\n", i == 1 ? ":" : "," ,names
[ i ], defaults[ i ] )
}
printf( " {\n" )
printf( " }\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
name = names[ i ]
type = types[ i ]
printf( " %s %s() const\n", type, name )
printf( " {\n" )
printf( " return my_%s;\n", name )
printf( " }\n" )
printf( " Options& %s( %s new_%s )\n", name, type,
name )
printf( " {\n" )
printf( " my_%s = new_%s ;\n", name, name )
printf( " return *this ;\n" )
printf( " }\n" )
}
printf( "private:\n" )
for ( i = 1 ; i <= optionCount ; ++ i ) {
printf( " %s my_%s ;\n", types[ i ], names[ i ] )
}
printf( "} ;\n" )
}
, invoked with "genOptions.awk -v className=Options
options.lst", is less than 50 lines (and would be less if I'd
compromize on the formatting of the generated class),
Well 150 lines of code isn't much to talk about, really. :)

If it's fairly trivial and easy to understand:).
Only where it doesn't matter: for producing that support code
in the first place.
For using it I think an all C++ solution such as my Friday
code is much simpler, both with respect to using it from
scratch, with respect to maintainance (no redundancy), and
with respect to build configuration.

For using it, there is one argument in favor of all C++, if it's
straightforward: presumable new people on the project will
understand it immediately. IMHO, as soon as you use Boost::mpl,
or Loki, you've lost that advantage. As for maintenance: the
input file for my AWK script looks something like:

x double 3.14159
y int 42
# ...

It's hard for me to imagine anything easier to maintain---to add
a new attribute, it's one line. (In the form I actually use,
the AWK script is about twice as long, and the input has the
form:

[Option] # the class name...
x double 3.14159
y int 42
# ...

The results are two files, Option.hh and Option.cc.)

Of course, if you really want pure C++... I've also got classes
to facilitate used C++ programs to generate C++, and with even a
minimum of the usual C++ stuff, rewriting the AWK in C++
shouldn't pose any great problem. In this case, the AWK doesn't
really use any of the special functionality of AWK. It's just
that for me, for such small programs, I find AWK simpler. (I
have cases where the generation is significantly more complex,
in which case, I do use C++.)
This does not produce an extensible class.

Extensible in what way?
In particular it can not, AFAICS, be used to produce the two
option classes in my example usage code shown earlier in
thread (natural modifications OK, of course), which you'll see
if you try.

I'll look at it, but I'll admit that I've never found any reason
to "extend" an options class. They're normally one-of sort of
things.
But you make me wonder: what does the script buy you over the
above, which AFAICS offers the same functionality with less
code and easier maintainance?

First, the AWK stuff doesn't use macros, so doesn't impinge on
the global namespace in anyway. (My actual AWK script handles
options to specify the namespace in which to build the class,
files to include, etc.) And the AWK stuff generates a complete
class; there's absolutely nothing else to do. Still, for a
one-of use, I'd use the above; the AWK script came about because
it fulfilled a more or less recurrent need.
If that's all that you require of the class then that scheme
is superb, because it's simple. :) But the little
inefficiency is not necessary. Just replace Fallible with a
class that constructs the contained value with the specified
default.

But then you have to maintain the default outside of the macro
declarations. IMHO, it's not usually a big deal, but it does
mean that you have to cite the name of the attribute twice, and
update in two locations if you add one.
Of course you need one such class per option, even when the
options are of the same type. And then you have the basic
options-as-types scheme... ;-)

I'm not sure I follow. You need one class for each distinct set
of options. Typically, in a GUI, there will only be one or two
distinct sets of options... in a lot of GUI's, there will only
be one (although some components may ignore certain options).
But the above is not a superb solution to my little problem,
for it does not let you extend the resulting class except by
requiring the code that uses the setters to always use base
class setters last, which to me feels very wrong.
I think that problem indicates that there's a missing feature
in C++, because it "should" be trivial to express, and indeed
is trivial in any scripting language.

The basic problem is that you're "generating" code. Which is
fairly simple to express in interpreted languages like Lisp or
the shell, which can interpret the code you've just generated.
(In classical computer science, this is called "self modifying
code", and has a very bad reputation with regards to
maintainability. There are very good reasons why such
interpreted languages aren't used for critical systems.) I've
one or two applications which use such techniques, but they're
written in Unix shell, not in C++. (In one case, I have the
moral equivalent of "awk -f someProgram.awk file1 | awk -f -
file2 | isql", the awk script "someProgram.awk" reads a
configuration file to generate an awk script which is used to
generate SQL. It some ways, it's very elegant, but it can be a
real pain in the ass to maintain. Finding a bug in the SQL is a
pain, when the SQL is generated dynamically at each execution.)
 
A

Alf P. Steinbach

* James Kanze:
Because some other tool does the job better? "Pure C++" isn't a
particular goal in my mind, any more than "pure OO" or pure
anything else. I use the tools best fitted for the job.

Avoiding extra languages is a worthwhile goal.

With extra languages other people need to be familiar with them; you have code
generation which means that you have extra files that must be managed and must
not be modified; the build process needs to accomodate the code and you have at
least one extra build step; anyone wishing to use your code needs the tools for
the extra language, which may not be easily available; so on.

I see that as just needless complication & restriction.

Why is it necessarily a flaw in the language? No tool can be
perfect for everything. Use the most appropriate tool.

Simple things should be simple to express.

C++ isn't an inappropriate language, but that's only due to the factors that
count against using physical code generation.


[snip]
Well, that sounds like another reason to use something else. I
wouldn't use a tool that I didn't at least vaguely understand.

*Hark*.

I find it difficult to believe that you're unfamiliar with typelists.

But anyway, a pencil is a pencil is a pencil, there is nothing mysterious about
them. If the pencil you grab to write something with is covered with very small
cactus-like things that hurt your fingers, then, after looking around to check
whether there's any other pencil near you, you simply wrap the pencil in
something, and perhaps remark: "huh, that's odd, why the heck did they design
the pencil that way". It's less work to wrap than to go buy a set of pencils.

(It's a difficult issue, of course---I suspect that most C++
programmers wouldn't be capable of writing a C++ compiler, and
they shouldn't need that knowledge.

In contrast it's trivial to write a typelist implementation that provides basic
functionality, and any C++ programmer should be able to do so: any C++
programmer should be able to just sit down and write it without any second thought.

But I don't even know what
Boost::mpl---or anything in Loki---really does.)

Boost::mpl is much more than typelists.

But.

A typelist is a list of types. There are two main ways to build a typelist:
using the idea of a list as a head + a tail, which is a recursive definition,
and using the idea of a list as an array, which requires defaulting on template
parameters. The latter is what MPL does for the general 'list' type constructor,
which is the reason that a computed MPL empty list has a different type.


[snip]
For using it, there is one argument in favor of all C++, if it's
straightforward: presumable new people on the project will
understand it immediately. IMHO, as soon as you use Boost::mpl,
or Loki, you've lost that advantage.

Do you need to understand the details of the innards of your TV to use it?

I haven't found any need to be TV engineer (whatever) in order to use a TV.

Similarly, one does not need to understand template metaprogramming in order to
use something (such as my code) that uses that internally.

If you start looking at the inner parts that you don't understand, and think
that the high-voltage thingies etc. make the TV very dangerous, and that the
complexity of the circuitry inside makes the TV unsuable, well that's wrong.

Simply don't peer into the innards, use the controls at the front of the TV! :)

As for maintenance: the
input file for my AWK script looks something like:

x double 3.14159
y int 42
# ...

It's hard for me to imagine anything easier to maintain---to add
a new attribute, it's one line. (In the form I actually use,
the AWK script is about twice as long, and the input has the
form:

[Option] # the class name...
x double 3.14159
y int 42
# ...

The results are two files, Option.hh and Option.cc.)

With C++ you add one line and modify the line that defines the class, e.g., with
JK as James Kanze macro prefix:

JK_DEF_OPT( x, 3.14159 )
JK_DEF_OPT( y, int, 42 )
JK_DEF_OPT_CLS_2( Option, nobase, x, y )

The result is generated code at the point of those macro invocations, no extra
files to care about and manage, no generated files to remember to not modify.

E.g. the code can be generated inside any desired namespace or class.


[snip]
Extensible in what way?

My up-thread usage example's definition was

CPPX_DEFINE_OPTION( x, double )
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )

CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )

where the last line expresses a base-derived class relationship.

I'll look at it, but I'll admit that I've never found any reason
to "extend" an options class. They're normally one-of sort of
things.

So, this isn't the problem you've been dealing with. :)

First, the AWK stuff doesn't use macros, so doesn't impinge on
the global namespace in anyway. (My actual AWK script handles
options to specify the namespace in which to build the class,
files to include, etc.) And the AWK stuff generates a complete
class; there's absolutely nothing else to do. Still, for a
one-of use, I'd use the above; the AWK script came about because
it fulfilled a more or less recurrent need.

Namespace contamination is a valid concern, but it's minimal.

That the AWK stuff generates a complete class is not an argument in its favor.
It only indicates that there something so AWKward ;-) about it that generating a
complete class is somehow seen as an accomplishment. The C++ macros I presented
of course also generate a complete class.

And the C++ macros that I presented don't suffer from the problems of the AWK
script, i.e. with a proper C++ solution

* you don't generate extra files to worry about,

* you don't have extra tool requirements and extra tools usage, and

* you don't restrict the generated class' namespace or container class,
which with C++ is just where you invoke the macro.

But then you have to maintain the default outside of the macro
declarations.
No.


IMHO, it's not usually a big deal, but it does
mean that you have to cite the name of the attribute twice, and
update in two locations if you add one.


I'm not sure I follow. You need one class for each distinct set
of options. Typically, in a GUI, there will only be one or two
distinct sets of options... in a lot of GUI's, there will only
be one (although some components may ignore certain options).

The reason you're not sure you follow is probably that you've interjected
comments so as to take the statement you don't follow out of context.

I was taking about replacing the Fallible class you used in your example.

I.e. replacing Fallible with something that for the purpose is simpler and more
efficient (actually I've already presented complete code for that).

The basic problem is that you're "generating" code. Which is
fairly simple to express in interpreted languages like Lisp or
the shell, which can interpret the code you've just generated.
(In classical computer science, this is called "self modifying
code", and has a very bad reputation with regards to
maintainability.

Uh oh, careful now. The code isn't self-modifying. The code isn't the kind that
has a bad reputation. :)


Cheers,

- Alf
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* James Kanze:

[snip]
Extensible in what way?

My up-thread usage example's definition was

CPPX_DEFINE_OPTION( x, double )
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )

CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )

where the last line expresses a base-derived class relationship.

I'll look at it, but I'll admit that I've never found any reason
to "extend" an options class. They're normally one-of sort of
things.

So, this isn't the problem you've been dealing with. :)

Perhaps an example can help you/others understand the intentded usage.

In MainWindow constructor (this is just ephemeral try-it-out code) there is

new Edit( *this, Edit::params()
.text( L"Blah blah" )
.position( Point( 10, 10 ) )
.size( Point( 300, 20 ) )
);
new Button( *this, Button::params()
.text( L"My button" )
.position( Point( 10, 40 ) )
.size( Point( 160, 24 ) )
.isDefault( true )
);

where the Params passed to the Edit control and the Params passed to the Button
control have the same .text, .position and .size elements, because they're
derived from the same base general window Params class.

The Params passed to the Button control have an additional .isDefault, which is
not meaningful for the Edit control and hence doesn't exist in Edit::params.

In class Button the extra .isDefault option is added thusly, in a nested class
ApiWindowFactory:

class ApiWindowFactory
: public Outer::Base::ApiWindowFactory
{
typedef Outer::Base::ApiWindowFactory Base;
private:
bool myIsDefaultButton;

public:
CPPX_DEFINE_OPTION_VALUE( isDefault, bool, false )
CPPX_DEFINE_1OPTIONCLASS( Params, Base::params, isDefault )

ApiWindowFactory(
Window& parentWindow,
Params const& params = Params()
)
: Base( parentWindow, params )
, myIsDefaultButton( params.isDefault() )
{}

virtual ButtonKind::Enum apiButtonKind() const
{
CPPX_IS_OVERRIDE_OF( Base::apiButtonKind );
return (myIsDefaultButton? ButtonKind::defPushButton :
ButtonKind::pushButton);
}
};

As you can see defining the derived Params with an extra .isDefault, adding in
derived class setters for all the base class elements (.text, .position, .size
etc.), is very very simple, two lines of code, placed right where the definition
is desired -- and no external script or extra files or ... :)

There is a limitation of the macro implementations I posted earlier that the
base class can't be specified via typedef, but I don't consider that a problem;
rather, the problem is that I would want a simpler macro implementation.


Cheers,

- Alf
 
Ad

Advertisements

J

James Kanze

* James Kanze:
Avoiding extra languages is a worthwhile goal.
Why?

With extra languages other people need to be familiar with
them;

True. In the end, it's a trade-off. But anyone who knows C++
can learn AWK in an hour or so, and it's a generally useful
tool. And of course, you'll end up using extra languages
anyway; you're not going to write all of the C++ by hand.

Also, of course, with the correct tool kit, the AWK can be
transcribed into C++ with little extra work. (See things like
FieldArray, at my site. That's really all you'd need in this
case, but I also have a class for creating C++ sources, which
manages namespaces and include files, and automatically
generates the header (with copyright), footer (with
configuration information for emacs and vim), and if the
filename ends with .*hh, .hpp, .hxx, .h or .H, the include
guards. (Which means that the results would be more complete
than with my AWK script.)

Again. If it's just a one-of situation, I'd just use the
macros. If it's something that may occur again, investing a
little more time (not much) and using an external generator is
probably more effective in the long run.
you have code generation which means that you have extra
files that must be managed and must not be modified; the build
process needs to accomodate the code and you have at least one
extra build step; anyone wishing to use your code needs the
tools for the extra language, which may not be easily
available; so on.

But in any non-trivial project, you're going to have code
generation anyway. The only question is what tool to use. That
was behind my question as to what Windows programmers habitually
use.
I see that as just needless complication & restriction.
Simple things should be simple to express.

It is simple to express. In AWK. Or in C++, if you've got the
right tools available. What you seem to want, however, is that
the program to generate the code be expressed in the same
program which it generates; that's what creates the complexity.

That always creates a complexity. If you need to do it (e.g.
because the program generating the code needs information, such
as type information, resulting from compiling other parts of the
code), then you do it. Otherwise, using an external program is
generally preferable for all but the simplest generation. It
does add a little bit of complexity to the build
procedure---when I do such things, I usually have to add two or
three lines to the makefile, manually. But keeping the
generation logic separate from the rest of the program makes
things much easier to read and understand.
C++ isn't an inappropriate language, but that's only due to
the factors that count against using physical code generation.

C++ isn't an inappropriate language, really. It's just a little
heavy, given the simplicity of the generation program---using
C++ for the external generation complicates the build procedure
even more.

What complicates your code is the fact that you're trying to
embed two totally independent things---the code generation, and
the program you want to compile in the end---in a single source
file. My experience with this sort of thing (e.g. TeX, or the
text macros in Intel's original ASM-86) is that it rapidly
becomes confusing.
[snip]
Well, that sounds like another reason to use something else.
I wouldn't use a tool that I didn't at least vaguely
understand.

I find it difficult to believe that you're unfamiliar with
typelists.

I've heard the name, but that's about it. I don't doubt that I
could learn them, if I had any reason to, but until now, I've
not had any reason to.
But anyway, a pencil is a pencil is a pencil, there is nothing
mysterious about them. If the pencil you grab to write
something with is covered with very small cactus-like things
that hurt your fingers, then, after looking around to check
whether there's any other pencil near you, you simply wrap the
pencil in something, and perhaps remark: "huh, that's odd, why
the heck did they design the pencil that way". It's less work
to wrap than to go buy a set of pencils.

But I've already got lot's of other pencils laying around:).
For starters, AWK is installed on all of the machines I have
access to, and I know it pretty well. And if for some reason I
didn't want to use AWK, one of the first things I did when I
started using C++ was write classes to support most of what AWK
does: String (this was long before std::string existed),
RegularExpression and FieldArray (which breaks a string up into
fields). Again, the toolset is there---you could, in fact,
easily implement what I did in AWK using nothing more than
iostream and std::string (although I'd probably throw in
boost::regex for good measure).

For a number of historical reasons, my code is designed to be
built in a Unix-like environment. AWK is there, I know it, and
it is simpler to insert into the build process than C++ code
(which means you have to compile and link a separate program).
So I use it. But the important aspect, in my mind, is that the
code which generates the code is completely separate from what
it generates.
In contrast it's trivial to write a typelist implementation
that provides basic functionality, and any C++ programmer
should be able to do so: any C++ programmer should be able to
just sit down and write it without any second thought.

Maybe. Given that all I know about it is the name, I can't
judge. I don't really even know what it does, exactly. (I'm
guessing that it's some sort of list of types, but I don't
really know what that means, or how it is used.)
Boost::mpl is much more than typelists.

A typelist is a list of types. There are two main ways to
build a typelist: using the idea of a list as a head + a tail,
which is a recursive definition, and using the idea of a list
as an array, which requires defaulting on template parameters.
The latter is what MPL does for the general 'list' type
constructor, which is the reason that a computed MPL empty
list has a different type.

The former sounds like Lisp:).
[snip]
For using it, there is one argument in favor of all C++, if
it's straightforward: presumable new people on the project
will understand it immediately. IMHO, as soon as you use
Boost::mpl, or Loki, you've lost that advantage.
Do you need to understand the details of the innards of your
TV to use it?
I haven't found any need to be TV engineer (whatever) in order
to use a TV.
Similarly, one does not need to understand template
metaprogramming in order to use something (such as my code)
that uses that internally.
If you start looking at the inner parts that you don't
understand, and think that the high-voltage thingies etc. make
the TV very dangerous, and that the complexity of the
circuitry inside makes the TV unsuable, well that's wrong.
Simply don't peer into the innards, use the controls at the
front of the TV! :)

But someone has to maintain the code. If it's part of an
externally provided library, then the library clients don't have
to understand it---I don't usually look at the internals of the
libraries I use. (The one exception is std::string of g++.
Because I was told it used reference counting, and was fully
thread safe. And it was suggested by other people, e.g. Herb
Sutter, that this wasn't possible. And in fact, g++'s
implementation of std::string isn't thread safe.)
As for maintenance: the input file for my AWK script looks
something like:
x double 3.14159
y int 42
# ...
It's hard for me to imagine anything easier to maintain---to add
a new attribute, it's one line. (In the form I actually use,
the AWK script is about twice as long, and the input has the
form:
[Option] # the class name...
x double 3.14159
y int 42
# ...
The results are two files, Option.hh and Option.cc.)
With C++ you add one line and modify the line that defines the
class, e.g., with JK as James Kanze macro prefix:
JK_DEF_OPT( x, 3.14159 )
JK_DEF_OPT( y, int, 42 )
JK_DEF_OPT_CLS_2( Option, nobase, x, y )
The result is generated code at the point of those macro
invocations, no extra files to care about and manage, no
generated files to remember to not modify.

You're still mentionning x and y twice:). So you have to
insert one line, and modify another; in my version (both macros
and AWK), you just insert a single line.

Anyway: I'm supposing that the _2 in the last line specifies the
number of attributes. IMHO, that's error prone, but get rid of
that, and I think I could buy it.

Don't forget that I developped my solution with AWK before
compilers supported templates. Of any sort. And it works, so
I've had no motivation to change it. And I still suspect that
the AWK code, even with all of the options I've given it, is a
lot simpler to understand and maintain than your templates and
macros.
E.g. the code can be generated inside any desired namespace or
class.

My AWK script actually handles that as well:). But that does
make it more complicated. The alternative is to include the
generated code in the context you want.
[snip]
Extensible in what way?
My up-thread usage example's definition was
CPPX_DEFINE_OPTION( x, double )
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )
CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )
where the last line expresses a base-derived class relationship.

I could easily add that to my script, but I've never needed it.
(It's already implicitly there with the macro solution.)
So, this isn't the problem you've been dealing with. :)

As I said, I've never needed it.
Namespace contamination is a valid concern, but it's minimal.

Agreed. All those prefixes don't look pretty, but the get the
job done, and they don't occur in the middle of complex
expressions, or other places where they would lead to confusion.
That the AWK stuff generates a complete class is not an
argument in its favor. It only indicates that there something
so AWKward ;-) about it that generating a complete class is
somehow seen as an accomplishment. The C++ macros I presented
of course also generate a complete class.

When I said "generates a complete class", I meant that it
generates everything you'd write if you wrote a class yourself:
a header file and a source file, with all of the includes, etc.
necessary.
And the C++ macros that I presented don't suffer from the
problems of the AWK script, i.e. with a proper C++ solution
* you don't generate extra files to worry about,

If it's a separate class, don't you want it in a separate
header, with a separate source file?

(Seriously, I think it depends on the application. In my case,
the generated class was an important class in a fairly large
library, and it definitely made sense for it to behave vis-a-vis
client code like any other class in the library, with it's own
header, etc. I can imagine that in other cases, other solutions
would be preferred.)
* you don't have extra tool requirements and extra tools
usage, and

To which I say: so what? In practice, I already have the tool
requirements, since I require GNU make for my makefiles, and a
number of other Unix tools for things like generating and
running test suites, cleaning up flex output so it passes modern
C++ compilers, generating the documentation (using Doxygen, but
with significant pre- and post-processing), etc.

You can't develop a project with just a compiler; you need a
tool kit of some kind. (Again, that was why I asked what
Windows programmers use. You do need something.)
* you don't restrict the generated class' namespace or container class,
which with C++ is just where you invoke the macro.

My personal scripts allow for this as well (although they won't
generated the class as a nested class), but I'll admit that your
solution does seem simpler in this respect.
The reason you're not sure you follow is probably that you've
interjected comments so as to take the statement you don't
follow out of context.
I was taking about replacing the Fallible class you used in
your example.
I.e. replacing Fallible with something that for the purpose is
simpler and more efficient (actually I've already presented
complete code for that).

OK. You mean using a separate type for each option (and, I
suppose, putting the default value in the constructor for the
type). I'd missed that.

You still have to declare that type, before declaring the
option.
Uh oh, careful now. The code isn't self-modifying. The code
isn't the kind that has a bad reputation. :)

Well, yours isn't self modifying in the traditional sense,
because in C++, templates are completely evaluated by the
compiler, but the Lisp versions are, sort of. More importantly,
while neither is really considered the self-modifying that has
the bad reputation, IMHO, there's a real sense that at least
some meta-programming is: when you look at the source code, what
you see is not what you get. (I don't think that your code, at
least as I finally understand it, suffers from this---at least
not if you've documented the macros sufficiently.)
 
J

James Kanze

* Alf P. Steinbach:
* James Kanze:
[snip]
Extensible in what way?
My up-thread usage example's definition was
CPPX_DEFINE_OPTION( x, double )
CPPX_DEFINE_OPTION( y, double )
CPPX_DEFINE_2OPTIONCLASS( Pos2Options, cppx::eek:ptions::NoBase, x, y )
CPPX_DEFINE_OPTION( z, double )
CPPX_DEFINE_1OPTIONCLASS( Pos3Options, Pos2Options, z )
where the last line expresses a base-derived class relationship.
In particular it can not, AFAICS, be used to produce the
two option classes in my example usage code shown earlier
in thread (natural modifications OK, of course), which
you'll see if you try.
I'll look at it, but I'll admit that I've never found any
reason to "extend" an options class. They're normally
one-of sort of things.
So, this isn't the problem you've been dealing with. :)
Perhaps an example can help you/others understand the intentded usage.
In MainWindow constructor (this is just ephemeral try-it-out code) there is
new Edit( *this, Edit::params()
.text( L"Blah blah" )
.position( Point( 10, 10 ) )
.size( Point( 300, 20 ) )
);
new Button( *this, Button::params()
.text( L"My button" )
.position( Point( 10, 40 ) )
.size( Point( 160, 24 ) )
.isDefault( true )
);
where the Params passed to the Edit control and the Params
passed to the Button control have the same .text, .position
and .size elements, because they're derived from the same base
general window Params class.
The Params passed to the Button control have an additional
.isDefault, which is not meaningful for the Edit control and
hence doesn't exist in Edit::params.

OK. I rather suspect that this could have been used in my case
as well. In my case, most of the components shared a very large
set of options, and have few, if any, additional options (one or
two at the most), so we just used an Options class for the
common options, and additional arguments (with defaults) for the
one or two additional attributes, when present. But your
solution seems more elegant.
In class Button the extra .isDefault option is added thusly,
in a nested class ApiWindowFactory:
class ApiWindowFactory
: public Outer::Base::ApiWindowFactory
{
typedef Outer::Base::ApiWindowFactory Base;
private:
bool myIsDefaultButton;
public:
CPPX_DEFINE_OPTION_VALUE( isDefault, bool, false )
CPPX_DEFINE_1OPTIONCLASS( Params, Base::params, isDefault )
ApiWindowFactory(
Window& parentWindow,
Params const& params = Params()
)
: Base( parentWindow, params )
, myIsDefaultButton( params.isDefault() )
{}
virtual ButtonKind::Enum apiButtonKind() const
{
CPPX_IS_OVERRIDE_OF( Base::apiButtonKind );
return (myIsDefaultButton? ButtonKind::defPushButton :
ButtonKind::pushButton);
}
};
As you can see defining the derived Params with an extra
.isDefault, adding in derived class setters for all the base
class elements (.text, .position, .size etc.), is very very
simple, two lines of code, placed right where the definition
is desired -- and no external script or extra files or ...
:)

Yep. And my solution doesn't support generating a nested class
(and making it do so would be almost impossible).

I still don't agree with your objections because an extra tool
is used---I'd rather use an extra tool than do extra work
myself. But in this case, you've pretty much convinced me that
your solution does something the "simpler" solution with the
extra tool can't, which is a strong argument in its favor.
 
J

James Kanze

(e-mail address removed)>, (e-mail address removed)
says...
[ ... ]
BTW: What is the Windows equivalent of AWK? There must be
one, or do all Windows programmers have CygWin or something
similar installed?
Nah -- various versions of AWK have been compiled natively for
Windows (and even MS-DOS) for years.

I know that. I was using a Unix-like tool kit under MS-DOS in
the late 1908's. But I'm hardly a typical Windows user; I was
wondering what typical Windows developers use.
One project that has ported a reasonable number of the major
GNU utilities to Windows (native, not Cygwin) is at:

This includes both gawk and m4, either of which would be quite
adequate for jobs like this.

Yes. I mentioned CygWin more as a placeholder for any Unix-like
tool kit, rather than with the idea of CygWin, and only CygWin.
I'm aware of at least three others: UWin, MSys and MKS (the
latter commercial, and rather expensive---although it wasn't too
expensive in the MS-DOS days), and I don't doubt that there are
others. (I'm still looking for one which integrates well,
however. Most of them seem to go out of their way to make it
difficult to use tools other than those in the toolset. And if
I'm running Windows, and not Linux, on a machine, it's because I
want to use the Windows tools when appropriate.)
 
Ad

Advertisements

A

Alf P. Steinbach

* James Kanze:
I find it difficult to believe that you're unfamiliar with
typelists.

I've heard the name, but that's about it. I don't doubt that I
could learn them, if I had any reason to, but until now, I've
not had any reason to.
[snip]
In contrast it's trivial to write a typelist implementation
that provides basic functionality, and any C++ programmer
should be able to do so: any C++ programmer should be able to
just sit down and write it without any second thought.

Maybe. Given that all I know about it is the name, I can't
judge. I don't really even know what it does, exactly. (I'm
guessing that it's some sort of list of types, but I don't
really know what that means, or how it is used.)

Typelists were popularized by Andrei. I think he invented them independently but
they already had some history. What Andrei did was to show how all the usual run
time control structures had analogs in template metaprogramming, and to build up
from that to a framework handling types just as for run time you handle values.

I made a basic example "the minimal typelist" :), shown below.

It may interest other readers of the group (of course what this example does is
very simple, intentionally, whereas the inheritance over a list of types in the
option macro stuff I posted earlier is one or two degrees more complex):


<code>
#include <iostream>
#include <limits>

struct NoSuch {};

// General Lisp-like typelist
template< class HeadType, class TailType = NoSuch >
struct Typelist_
{
typedef HeadType Head;
typedef TailType Tail;
};

template< class T >
void showMaxValue()
{
using namespace std;
cout << numeric_limits<T>::max() << endl;
}

template< class Typelist >
void showMaxValues()
{
showMaxValue< typename Typelist::Head >();
showMaxValues< typename Typelist::Tail >();
}

template<> void showMaxValues<NoSuch>() {}

int main()
{
typedef Typelist_<int, Typelist_<float, Typelist_<double> > > Types;
showMaxValues<Types>();
}
</code>


Cheers,

- Alf
 

Top