
On Mon, Nov 14, 2011 at 11:35 PM, Paul Mensonides <pmenso57@comcast.net> wrote:
On Mon, 14 Nov 2011 19:57:31 +0000, Jens Gustedt wrote:
I think any macros that are designated to be used by an "end user" must mediate between those different views and provide an end result that reads well for that end user, who I think expects syntax written as if it where core language.
In P99 I use the IS_EMPTY macro for exactly that, try to accommodate that supposed expectation.
I strongly disagree that catering to that expectation is a good thing in any context. The sooner people realize (and expect) that the preprocessor behaves differently than the core language, the better. Virtually all issues related to the misuse of the preprocessor stems from attempting to make object-like macros look like constant variables and function-like macro invocations look like underlying-language function calls. At best, the correlation between function-like macro invocations and function calls should be incidental. It should never be considered to be a goal. That is a fundamentally broken mentality.
Preprocessing directives and macro invocations are part of an EL (DSEL without the DS) that overlays the underlying language; they are not part of the syntax or semantics of the underlying language. The preprocessing phases of translation are best thought of as a executing a program overlaid on the source that translates that source into a token sequence presented to the C or C++ parser. Anything else is just perpetuating the problem.
While it is not my business to force anyone to do anything, I am certainly not going to write code which may be viewed as best practice that intentionally perpetuates that mentality. And this really is about that mentality. It isn't about the slight syntactic inconvenience. Sure, with a sequence (a)(b)(c), that's a lot of extra parentheses (and you get other benefits with sequences such as elements containing commas and data structures with unbounded length), but a tuple (a,b,c) does not have a lot of extra parentheses. You'd get MACRO() vs. MACRO((a,b,c)) which has an incidental two extra parentheses and the two invocation cases are unambiguously, compiler-error-free detectable as different. The only supposed "downside" is that it doesn't look like a regular function call which is a symptom of the above mentality and is not actually a downside (it might even be an upside given the status quo which has been around since the early days of C).
With an interface macro without a certain domain (where the necessary domain restrictions are reasonable), it really depends on the reuseability level of the interface. Otherwise, there are serious repercussions related to recursion inherent in any forwarding interface (i.e. convert- and-forward) unless you do something with recursion like Chaos does which requires an extremely good preprocessor.
In the typical case, it is never a good idea, even at the interface level. In some more advanced cases, it isn't that big a deal. For example, in Lorenzo's (sp?) contract programming library, the syntax is so blatantly not that of the underlying language and the domain is specific enough that he can get away with it without it being particularly adverse. In that case, the resulting syntax is blatantly a DSEL and therefore can have whatever syntax it wants since confusion between different "languages" is nearly impossible. That scenario, however, is
For example: CONTRACT_FUNCTION_TPL( public void (push_back) ( (const T&) val ) precondition( size() < max_size() ) postcondition( auto old_size = CONTRACT_OLDOF size(), auto old_capacity = CONTRACT_OLDOF capacity(), back() == val, requires boost::has_equal_to<T>::value, size() == old_size + 1, capacity() >= old_capacity ) ) { vect_.push_back(val); }
quite a bit different than some macro just defined as A(a, b, ...) as opposed to A((a, b, ...)) or A((a)(b)(c)) just to make it look like a function call.
--Lorenzo