
On Mon, 21 Feb 2011 18:40:17 -0500, Lorenzo Caminiti wrote:
On Mon, Feb 21, 2011 at 2:37 PM, Paul Mensonides <pmenso57@comcast.net>
I take serious issue with anything that intentionally perpetuates this mentality. It is one thing if the syntax required is the same by coincidence. It's another thing altogether when something is done to intentionally make it so.
It might be useful to discuss this topic using the Boost.Local `PARAMS` macro as an example.
IMO, the following syntax is better from a pp metaprogramming prospective because the arguments are a proper pp data structure (i.e., a sequence) and it is very clear from the syntax that you are invoking a macro:
int BOOST_LOCAL_FUNCTION_PARAMS( (int x) (const bind this) ) { // [1] ... } BOOST_LOCAL_FUNCTION_NAME(l) l(-1);
However, from the users' prospective the following syntax is preferred because it looks more like a C++ function parameter declaration so they are more familiar with it:
int BOOST_LOCAL_FUNCTION_PARAMS(int x, const bind this) { // [2] ... } BOOST_LOCAL_FUNCTION_NAME(l) l(-1);
Therefore, as a pp metaprogrammer I'd prefer [1] but as the Boost.Local library developer I must keep in mind my users' preference and also provide [2] when variadics are available.
There is such a thing as good/bad regardless of preference. In the second case above, you've traded compilation efficiency for a minor syntactic difference. As a library developer, particularly in the case of a general purpose library, that's a bad call to make. If you provide both of the above, at least you still have the efficient version that doesn't waste time doing a format conversion, but now you've introduced bloat into the interface. You have two ways to do the same thing--one of which is (apparently, given the existence of the former) non-portable as well as brittle as far as revision is concerned. What happens when you want to add some option--LOCAL_FUNCTION_PARAMS(option, (int x)...)? In the latter case, because the preprocessor has no type system, that change is radical. In the former case, you can do what I've been talking about with BOOST_PP_TUPLE_ELEM (et al) and produce a smooth, non-breaking revision path. Instead, you end up with yet another interface point LOCAL_FUNCTION_PARAMS_WITH_OPTION that avoids the problem while adding yet more interface bloat. Perhaps this LOCAL_FUNCTION_PARAMS is a bad example for potential revision, but the above is referring the general case. As I said previously, the only way that I would ever provide a macro interface (in a non-preprocessor metaprogramming library) such as the latter above, is if I was absolutely certain that no arguments would be added or removed.
IMO, it would be nice if Boost.Preprocessor supported variadics to make metaprogramming [2] easy. However, that does not necessarily mean providing:
BOOST_PP_VARIADIC_TUPLE(...)
I would find having these two macros just as useful (and perhaps more correct):
#define BOOST_PP_TO_TUPLE(...) (__VA_ARGS__) BOOST_PP_TUPLE((...))
What is BOOST_PP_TUPLE((...))?
Then at some point in my pp metaprogram, I will have `BOOST_PP_TUPLE(BOOST_PP_TO_TUPLE(__VA_ARGS__))` which would be as convenient for me (a pp metaprogrammer) to use as `BOOST_PP_TUPLE(__VA_ARGS__)` directly. Of course, the `BOOST_PP_TO_TUPLE(__VA_ARGS__)` invocation will be hidden inside `BOOST_LOCAL_FUNCTION_PARAMS(...)` expansion to respect my library users' request that the `PARAMS` macro invocation should look like a normal C++ function parameter declaration as much as possible.
It is a DSEL. By definition it has its own syntax--as it obviously already does.
In summary, I would think that providing `BOOST_PP_TO_TUPLE(...)` and `BOOST_PP_TUPLE((...))` is a good approach.
In the case of a tuple, why would any of the above be better than simply (__VA_ARGS__)? Again, though I don't know what you're referring to by BOOST_PP_TUPLE((...)). What is that macro supposed to do? Regards, Paul Mensonides