Topic: Preprocessor alternatives (was Global "if" statement?)
Author: videoman@tiac.net (Videoman)
Date: 1997/05/10 Raw View
On 05 May 97 03:23:26 GMT, danm@ziplink.net (Dan Muller) wrote:
>To do something like the above logging statement concisely without the
>processor, I could imagine a language extension requiring a new keyword
>that can only be used as a type in parameters of non-virtual inline
>functions:
>
>inline void logout (int flags, ostream& os, expression e)
>{
> if (flags & g_log_flags)
> os << e;
>}
>
>logout (LOG_WARN, log_os, "Alien spaceships landed at "
> << location << "; you have been warned." << endl);
>
>The keyword "expression" denotes an arbitrary expression that textually
>replaces e at compile-time. Since this is inline, if g_log_flags and
>a flags argument are both constants, a compiler can optimize the call out
>of existence entirely.
See my other post about meta-programming. Your example is about
perfect. Try this:
meta void logout (int flags, ostream& os, expression e)
{
if (flags & g_log_flags)
os << e;
}
It might be a bit easier on compilier-writers to introduce the "meta"
keyword in this case, so that they do not have to look-ahead to the
argument list and see the "expression" keyword.
There would still be a question of: Should flags and os be considered
completely compile-time parameters? If so, and the if expression
evaluates to true, should "os << e" be dropped into whatever scope
directly, or should it be wrapped in a nested block scope?
I could probably think of useful examples each way.
Perhaps it would be useful to use meta in front of all expressions,
etc, that should be evaluated at compile-time?
meta void logout (meta int flags, ostream& os, expression e)
{
meta if (flags & g_log_flags) // perhaps meta-ness could be
os << e; //determined by the fact that
//one of the operands is a
//meta-value?
}
I think it is clearly undesireable to turn the source code into
meta-soup, however.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: danm@ziplink.net (Dan Muller)
Date: 1997/05/06 Raw View
In article <336E08D7.39F79CCC@physik.tu-muenchen.de>, celtschk@physik.tu-
muenchen.de says...
> What about this solution:
>
> class flags_t
> {
> int Flags;
> public:
> flags_t(int f): Flags(f) {}
> operator int() { return Flags; }
> operator bool() { return Flags & g_log_flags; }
> } flags;
>
> ...
>
> if(flags) log_os << "You have been warned!" << endl;
Well, this in particular doesn't meet my needs, since the flags vary from
one log statement to another. But I can see how this idea can be extended
to make a pretty concise syntax for this application.
The more general question remains, though. One thing that the
preprocessor can do, and the language cannot, is allow you to pass an
arbitrary expression (or even partial expression -- yech) into a macro
and have it pasted in the midst of some boilerplate code. In spite of
looking like a function call, the expression used as an argument may or
may not be evaluated at run-time, depending on the macro expansion. This
can be very useful, but one of the things that often trips up macro
authors is that the expression is evaluated in the context created by
that code.
I'm intrigued by the possibility of a language extension based on
functions that could address this, by deferring evaluation at the point
of call, putting that evaluation under control of the code that receives
it, and yet have the expression evaluated in the identifier namespace of
the point of call. It's sort of like a static version of closures, a
feature of LISP languages.
I understand that these semantics can be achieved with additional work,
using classes to encapsulate context, but it's very inconvenient in cases
where the expression needing the context is one-of-a-kind, as in these
logging format expression. I'm not pushing an idea as a proposal, I'm
just curious to see if anyone else has thoughts on this.
--
Dan Muller danm@ziplink.net
http://www.ziplink.net/~danm
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: danm@ziplink.net (Dan Muller)
Date: 1997/05/05 Raw View
In article <336A5C5B.5858@ix.netcom.com>,
strip_these_words_pderocco@ix.netcom.com says...
> Steve Clamage wrote:
> >
> > For example, I suppose you were thinking of something like
> > if( INT_MAX < 0xFFFFFFFF )
> > typedef long INT;
> > else
> > typedef int INT;
> > Assuming existing semantics of "if", this example puts different
> > definitions of INT in the same scope and is not allowed.
> >
> > You could use braces:
> > if( INT_MAX < 0xFFFFFFFF ) {
> > typedef long INT;
> > } else {
> > typedef int INT;
> > }
> > but now INT is not visible in the scope where you want it.
> >
> > In other words, you would have what looks like normal syntax, but
> > it would need completely different semantic rules. I don't
> > remember ever seeing a proposal to invent such a replacement for
> > the preprocessor.
>
> That's true. Perhaps a different keyword than "if" should be used
> (groan). I'd suggest that a global "if" would really be a different
> animal, and the braces in your example wouldn't create a separate scope
> (just as extern "C" { ... } doesn't create a scope). It really would
> function more like the preprocessor #if, but representable as part of
> the parse tree.
I've been following this thread with interest, because I think that
eventually eliminating the preprocessor is a desirable goal. This would
simplify many ancillary development tools, and make compilation more
efficient, to name a couple of benefits.
Here are a few thoughts of my own on the topic:
In order for these "file-scope conditionals" to be of any use, they need
constants that they can test. Where do the constants come from? In some
cases, they can come from system-specific header files. But in other
cases, such as to control debugging statements, a typical build
environment will still want to control some of these at build-time, such
as debug flags, without modifying code. A simple way around this might be
to define a "system-level" scope, with the method of providing system-
level definitions left unspecified by the language. Compilers could then
allow you to set them with syntax similar to the currently popular
command-line preprocessor definitions, or by allowing you to specify a
header file name which is implicitly read before each source file. The
latter method is, I believe, already available in some compilers,
although of course there's no "system-level" scope, and it's very
flexible.
The reason that I introduce a "system-level" scope is because you would
probably want file-level scope to act as if it's nested within system-
level scope, so that source code definitions can hide system-level
definitions. This prevents unexpected name clashes between system-level
and file-level scope.
The disadvantage is that this takes the definitions right out of the
source files, which creates an unpleasant dislocation if you have
conditional constructs that only make sense within a single source file.
However, it's exactly the proliferation of such conditionals that leads
to code that's hard to maintain, so perhaps this isn't such a hardship. A
good practice (IMO) for low-level code that really must be different on
various platforms is to encapsulate it in separate files, one for each
platform, and have the build tools select the appropriate file at build
time. If you've restricted the scope of this code tightly by abstracting
a very low-level but still preprocessor-independent interface, then there
won't be much duplication between these files
There is at least one extra-language preprocessor construct that I'm
currently using which I would be loathe to give up. To facilitate logging
in some applications, I do something like this:
#define LOGOUT(_FLAGS, _LOGOS) if (!(g_log_flags & _FLAGS)) {} else
_LOGOS
...
LOGOUT(LOG_WARN,log_os)
<< "Alien spaceships landed at "
<< location << "; you have been warned." << endl;
The thing I like about this is that the messy and oft-repeated
conditional is replaced with a clean and simple syntax that looks like a
function call, and the formatting expression is still only evaluated when
needed. This sort of thing is hard to do concisely without the
preprocessor. The briefest preprocessor-free alternative that I can think
of is:
(LOG_WARN & g_log_flags) && g_log_os <<
<< "Alien spaceships landed at "
<< location << "; you have been warned." << endl;
... which perhaps isn't too bad, but looks a tad strange.
This sort of thing is, of course, not seen at file scope, so it's only
tangentially related to the proposal that started this thread. Pardon my
expansion of the topic.
To do something like the above logging statement concisely without the
processor, I could imagine a language extension requiring a new keyword
that can only be used as a type in parameters of non-virtual inline
functions:
inline void logout (int flags, ostream& os, expression e)
{
if (flags & g_log_flags)
os << e;
}
...
logout (LOG_WARN, log_os, "Alien spaceships landed at "
<< location << "; you have been warned." << endl);
The keyword "expression" denotes an arbitrary expression that textually
replaces e at compile-time. Since this is inline, if g_log_flags and
a flags argument are both constants, a compiler can optimize the call out
of existence entirely.
This is obviously very template-like, but I don't think that this can be
done easily with templates. Can anyone demonstrate otherwise?
--
Dan Muller danm@ziplink.net
http://www.ziplink.net/~danm
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/05/05 Raw View
Dan Muller wrote:
[...]
> There is at least one extra-language preprocessor construct that I'm
> currently using which I would be loathe to give up. To facilitate logging
> in some applications, I do something like this:
>
> #define LOGOUT(_FLAGS, _LOGOS) if (!(g_log_flags & _FLAGS)) {} else
> _LOGOS
>
> ...
>
> LOGOUT(LOG_WARN,log_os)
> << "Alien spaceships landed at "
> << location << "; you have been warned." << endl;
>
> The thing I like about this is that the messy and oft-repeated
> conditional is replaced with a clean and simple syntax that looks like a
> function call, and the formatting expression is still only evaluated when
> needed. This sort of thing is hard to do concisely without the
> preprocessor. The briefest preprocessor-free alternative that I can think
> of is:
>
> (LOG_WARN & g_log_flags) && g_log_os <<
> << "Alien spaceships landed at "
> << location << "; you have been warned." << endl;
>
> ... which perhaps isn't too bad, but looks a tad strange.
>
> This sort of thing is, of course, not seen at file scope, so it's only
> tangentially related to the proposal that started this thread. Pardon my
> expansion of the topic.
>
> To do something like the above logging statement concisely without the
> processor, I could imagine a language extension requiring a new keyword
> that can only be used as a type in parameters of non-virtual inline
> functions:
>
> inline void logout (int flags, ostream& os, expression e)
> {
> if (flags & g_log_flags)
> os << e;
> }
>
> ...
> logout (LOG_WARN, log_os, "Alien spaceships landed at "
> << location << "; you have been warned." << endl);
>
> The keyword "expression" denotes an arbitrary expression that textually
> replaces e at compile-time. Since this is inline, if g_log_flags and
> a flags argument are both constants, a compiler can optimize the call out
> of existence entirely.
>
> This is obviously very template-like, but I don't think that this can be
> done easily with templates. Can anyone demonstrate otherwise?
What about this solution:
class flags_t
{
int Flags;
public:
flags_t(int f): Flags(f) {}
operator int() { return Flags; }
operator bool() { return Flags & g_log_flags; }
} flags;
...
if(flags) log_os << "You have been warned!" << endl;
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/05/06 Raw View
Christopher Eltschka wrote:
>
> class flags_t
> {
> int Flags;
> public:
> flags_t(int f): Flags(f) {}
> operator int() { return Flags; }
> operator bool() { return Flags & g_log_flags; }
> } flags;
>
> ...
>
> if(flags) log_os << "You have been warned!" << endl;
I think the goal is to allow the compiler to optimize away the logging
operation entirely in the case where g_log_flags and flags are both
constants. I'd be surprised if a compiler was smart enough to do that in
this particular case.
--
Ciao,
Paul
(Please send e-mail to mailto:pderocco@ix.netcom.com instead of the
return address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]