Re: How can I use unqualified names? (Possibly hard or impossible?)
On Jul 26, 10:35 pm, "Alf P. Steinbach" <al...@start.no> wrote:
* James Kanze:
On Jul 26, 11:11 am, "Alf P. Steinbach" <al...@start.no> wrote:
* James Kanze:
On Jul 25, 11:23 am, "Alf P. Steinbach" <al...@start.no> wrote:
* James Kanze:
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).
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++?
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.
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.
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.
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]
<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.
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.
*Hark*.
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.
(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.
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.)
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.
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::options::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.)
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. :-)
As I said, I've never needed it.
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.
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 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.
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.
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).
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.
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.
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.)
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34