Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Where do you need expand macros in your C++ code?

106 views
Skip to first unread message

Thiago Adams

unread,
May 14, 2019, 9:56:58 AM5/14/19
to

C++ is always moving into a directing of removing the need for preprocessor.
Is it still necessary? Where do you need/use macros?

I am not considering #ifdef, #error etc, just macro expansion.

My answer for C++:

- assert
- We have REQUIRE_POINTER(pPointer) macro that returns an error if
the pointer is null.
- I am still using SIZEOF macro
- __FILE__ __LINE___
- Literal strings (I prefer macros than const char * )
- constants ( I am not using constexpr yet)
-







Bonita Montero

unread,
May 14, 2019, 11:18:29 AM5/14/19
to
> - I am still using SIZEOF macro

Before I used this ...

template<typename T, size_t N>
inline constexpr
size_t array_size( T (&)[N] )
{
return N;
}

... I write this ...

#define array_size(a) (sizeof(a) / sizeof(a[0]))

red floyd

unread,
May 14, 2019, 12:12:32 PM5/14/19
to
I use it to keep independent stuff in sync (though that's generally
in C, not C++)...

e.g.:

#define LIST_OF_STUFF \
ITEM(this1, value1, text1), \
ITEM(this2, value2, text2), // and so forth

#define ITEM(x, y, z) x = y
enum some_enum {
LIST_OF_STUFF
};

#undef ITEM
#define ITEM(x, y, z) #z
const char *const messages {
LIST_OF_STUFF
};

#undef ITEM





red floyd

unread,
May 14, 2019, 12:13:12 PM5/14/19
to
That is to say, as an X Macro.


Thiago Adams

unread,
May 14, 2019, 12:45:29 PM5/14/19
to
On Tuesday, May 14, 2019 at 1:12:32 PM UTC-3, red floyd wrote:
...
> I use it to keep independent stuff in sync (though that's generally
> in C, not C++)...

Very nice.

Since you mentioned C, In C I use a little more macros.

- X_INIT to initialize structs (In C++ this is not needed)
struct X x = X_INIT;
- constants
- Sometimes FOREACH
- custom Malloc/Free
- Unit Tests (just to simplify syntax)
-





Paavo Helde

unread,
May 14, 2019, 1:52:16 PM5/14/19
to
On 14.05.2019 16:56, Thiago Adams wrote:
>
> C++ is always moving into a directing of removing the need for preprocessor.
> Is it still necessary? Where do you need/use macros?
>
> I am not considering #ifdef, #error etc, just macro expansion.

1. For __FILE__, __LINE__:

#define MY_ASSERT(x) \
if (!(x)) throw AssertException(__FILE__, __LINE__, #x);

MY_ASSERT(n>0);

2. For avoiding forgetting to define a mutex lock variable:

#define BOOST_MUTEX_SCOPED_LOCK(mx) \
boost::mutex::scoped_lock boost_mutex_lock(mx)

boost::mutex mx1;
BOOST_MUTEX_SCOPED_LOCK(mx1);

// instead of non-functional
// boost::mutex::scoped_lock(mx1);

3. For dispatching execution to templates at run time:

#define DISPATCH_NUMERIC(ELEMTYPE,FUNCTION,ARGS) \
switch(ELEMTYPE) {\
case UInt8: {typedef uint8_t dispatch_t; FUNCTION ARGS; break;}\
case UInt16: {typedef uint16_t dispatch_t; FUNCTION ARGS; break;}\
case UInt32: {typedef uint32_t dispatch_t; FUNCTION ARGS; break;}\
case UInt64: {typedef uint64_t dispatch_t; FUNCTION ARGS; break;}\
case Int8: {typedef int8_t dispatch_t; FUNCTION ARGS; break;}\
case Int16: {typedef int16_t dispatch_t; FUNCTION ARGS; break;}\
case Int32: {typedef int32_t dispatch_t; FUNCTION ARGS; break;}\
case Int64: {typedef int64_t dispatch_t; FUNCTION ARGS; break;}\
case Float: {typedef float dispatch_t; FUNCTION ARGS; break;}\
case Double: {typedef double dispatch_t; FUNCTION ARGS; break;}\
default: throw Exception("Not implemented");\
};

template<typename T> foo(const T* source, size_t len, int param1);

DISPATCH_NUMERIC(
myvec.ElemType(), foo, (myvec.Data<dispatch_t>(), myvec.Size(), 42));

4. For compiler-specific stuff.

#ifdef _MSC_VER
#define DI_FOO __declspec(dllexport)
#else
#define DI_FOO
#endif

#if __GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=7)
#define OVERRIDE override
#else
#define OVERRIDE
#endif





Vir Campestris

unread,
May 14, 2019, 5:01:26 PM5/14/19
to
On 14/05/2019 14:56, Thiago Adams wrote:
> Where do you need/use macros?

I had one the other day.

We have two trace libraries, one that runs all the time and writes into
a memory buffer; the other writes to console, and can be turned on and off.

Sadly the APIs for them are not the same:

Logger logger("base.tag");
logger.log("tag", somethingorother)

results in a log tag "base.tag.tag" by joining the two tags together.

The other one doesn't have a basetag. So
otherlog("basetag.tag.tag", somethingorother) to make the tags match.

So
#define BASETAG "base.tag"
Logger logger(BASETAG);
logger.log("tag", somethingorother)
otherlog(BASETAG ".tag", somethingorother)

It _could_ be done with std::strings - but that's not the way these old
APIs with thousands of usages work :(

Andy

Alf P. Steinbach

unread,
May 14, 2019, 6:09:17 PM5/14/19
to
On 14.05.2019 19:52, Paavo Helde wrote:
> On 14.05.2019 16:56, Thiago Adams wrote:
>>
>> C++ is always moving into a directing of removing the need for
>> preprocessor.
>> Is it still necessary? Where do you need/use macros?
>>
>> I am not considering #ifdef, #error etc, just macro expansion.
>
> 1. For __FILE__, __LINE__:
>
> #define MY_ASSERT(x) \
>    if (!(x)) throw AssertException(__FILE__, __LINE__, #x);
>
> MY_ASSERT(n>0);

Consider using a macro to generate an instance of a class like <url:
https://en.cppreference.com/w/cpp/experimental/source_location>, and
pass that to the exception constructor. It centralizes the handling of
source location information. More DRY, Don't Repeat Yourself.

Maybe it will be part of C++23?

Of course, with that class' magic one will be able to ditch the macro.


> 2. For avoiding forgetting to define a mutex lock variable:
>
> #define BOOST_MUTEX_SCOPED_LOCK(mx) \
>    boost::mutex::scoped_lock boost_mutex_lock(mx)
>
> boost::mutex mx1;
> BOOST_MUTEX_SCOPED_LOCK(mx1);
>
> // instead of non-functional
> //  boost::mutex::scoped_lock(mx1);

Instead of one specific macro for each kind of thing one wants to have
constructor and destructor execution of, I suggest using just one or two
general macros.

C# and Java have this "with... some object"-concept as core language
features.

E.g. write

PH_WITH(boost::mutex::scoped_lock(mx)) {
// Code here
}


Can be defined in C++17 like

/// \brief Binds the specified declarator to `auto&& _` in the following
braces block.
///
/// `$with` can be used to make clear that an object is used for
constructor + destructor
/// execution (RAII), or that it's used just to hold a temporary result
in order to access
/// parts of it using the `_` name.

#define PH_WITH( ... ) \
if ( auto&& _ [[maybe_unused]] = __VA_ARGS__; true )

/// \brief Binds the specified declarator to `const auto& _` in the
following braces block.
///
#define PH_WITH_CONST( ... ) \
if( const auto& _ [[maybe_unused]] = __VA_ARGS__; true )



> 3. For dispatching execution to templates at run time:
>
> #define DISPATCH_NUMERIC(ELEMTYPE,FUNCTION,ARGS) \
> switch(ELEMTYPE) {\
> case UInt8: {typedef uint8_t dispatch_t; FUNCTION  ARGS; break;}\
> case UInt16: {typedef uint16_t dispatch_t; FUNCTION  ARGS; break;}\
> case UInt32: {typedef uint32_t dispatch_t; FUNCTION  ARGS; break;}\
> case UInt64: {typedef uint64_t dispatch_t; FUNCTION  ARGS; break;}\
> case Int8: {typedef int8_t dispatch_t; FUNCTION  ARGS; break;}\
> case Int16: {typedef int16_t dispatch_t; FUNCTION  ARGS; break;}\
> case Int32: {typedef int32_t dispatch_t; FUNCTION  ARGS; break;}\
> case Int64: {typedef int64_t dispatch_t; FUNCTION  ARGS; break;}\
> case Float: {typedef float dispatch_t; FUNCTION  ARGS; break;}\
> case Double: {typedef double dispatch_t; FUNCTION  ARGS; break;}\
> default: throw Exception("Not implemented");\
> };
>
> template<typename T> foo(const T* source, size_t len, int param1);
>
> DISPATCH_NUMERIC(
>   myvec.ElemType(), foo, (myvec.Data<dispatch_t>(), myvec.Size(), 42));

If you want to get rid of the C11 style macros, consider instead
something like

template< class F >
void dispatch_id_to_type( const int type_id, const F& f )
{
switch( type_id )
{
case Number_type::uint8_id: return f( uint8() );
case Number_type::uint16_id: return f( uint16() );
// ...
}
}

Here template parameter F will generally be the type of a lambda, like
from the call

dispatch_id_to_type(
myvec.ElemType(),
[&]( auto x ) {
using T = decltype( +x );
foo( myvec.Data<dispatch_t>(), myvec.Size(), 42 )
}()
);

Disclaimer: off the cuff code.


[snip]

Cheers!,

- Alf

Chris Vine

unread,
May 14, 2019, 6:26:28 PM5/14/19
to
On Tue, 14 May 2019 06:56:46 -0700 (PDT)
Thiago Adams <thiago...@gmail.com> wrote:
> C++ is always moving into a directing of removing the need for preprocessor.
> Is it still necessary? Where do you need/use macros?
>
> I am not considering #ifdef, #error etc, just macro expansion.

They can still be useful for simulating a function-like syntax with
normal order rather than eager evaluation of arguments.

My memoizing lazy singly-linked lists (which only evaluate a node when
the value has been explicitly requested by user code) make use of them,
in combination with lambda expressions to implement the lazy evaluation.

Chris

Bart

unread,
May 14, 2019, 6:38:19 PM5/14/19
to
On 14/05/2019 23:09, Alf P. Steinbach wrote:
> On 14.05.2019 19:52, Paavo Helde wrote:
>> On 14.05.2019 16:56, Thiago Adams wrote:
>>>
>>> C++ is always moving into a directing of removing the need for
>>> preprocessor.
>>> Is it still necessary? Where do you need/use macros?
>>>
>>> I am not considering #ifdef, #error etc, just macro expansion.
>>
>> 1. For __FILE__, __LINE__:
>>
>> #define MY_ASSERT(x) \
>>     if (!(x)) throw AssertException(__FILE__, __LINE__, #x);
>>
>> MY_ASSERT(n>0);
>
> Consider using a macro to generate an instance of a class like <url:
> https://en.cppreference.com/w/cpp/experimental/source_location>, and
> pass that to the exception constructor. It centralizes the handling of
> source location information. More DRY, Don't Repeat Yourself.
>
> Maybe it will be part of C++23?

Perhaps I'm missing something but, if you're going to build that into a
language, why do it the hard way?

I implement such things (not in C or C++), as:

f($filename, $lineno)

where these special names are known to the compiler and get substituted
for the actual module name, and actual source line number. No special
macros, no special classes, all very easy.

(However, the substitution occurs at the point where $lineno etc appears
in the source. In the example in your link:

void log(const std::string_view& message, const
std::experimental::source_location& location =
std::experimental::source_location::current())
{...

the current() location value passed is the one at the call-site, not
here. That may just be different ways that default arguments are generated.)

>> 3. For dispatching execution to templates at run time:
>>
>> #define DISPATCH_NUMERIC(ELEMTYPE,FUNCTION,ARGS) \
>> switch(ELEMTYPE) {\
>> case UInt8: {typedef uint8_t dispatch_t; FUNCTION  ARGS; break;}\
>> case UInt16: {typedef uint16_t dispatch_t; FUNCTION  ARGS; break;}\
>> case UInt32: {typedef uint32_t dispatch_t; FUNCTION  ARGS; break;}\
>> case UInt64: {typedef uint64_t dispatch_t; FUNCTION  ARGS; break;}\
>> case Int8: {typedef int8_t dispatch_t; FUNCTION  ARGS; break;}\
>> case Int16: {typedef int16_t dispatch_t; FUNCTION  ARGS; break;}\
>> case Int32: {typedef int32_t dispatch_t; FUNCTION  ARGS; break;}\
>> case Int64: {typedef int64_t dispatch_t; FUNCTION  ARGS; break;}\
>> case Float: {typedef float dispatch_t; FUNCTION  ARGS; break;}\
>> case Double: {typedef double dispatch_t; FUNCTION  ARGS; break;}\
>> default: throw Exception("Not implemented");\
>> };
>>
>> template<typename T> foo(const T* source, size_t len, int param1);
>>
>> DISPATCH_NUMERIC(
>>    myvec.ElemType(), foo, (myvec.Data<dispatch_t>(), myvec.Size(), 42));
>
> If you want to get rid of the C11 style macros,

My own view is that the above code should be taken out and shot.

(Each invocation would presumably repeat all this code at each
invocation point. But if it's only used once, then why use the macro.

However, there is also a clear pattern here, and there must be a better
way of doing it (whatever it is)). Even your template code seemed to
repeat the lines.

Ian Collins

unread,
May 15, 2019, 1:01:55 AM5/15/19
to
__FILE__ and line __LINE__

and a few macros passing them along with function names, functions and
arguments as parameters, such as

#define Check( fn, args ) \
utils::Exception::check( #fn, fn args, __FILE__, __LINE__ )

where check is

static int check( const char* fn,
int result,
const char* file,
int line );

--
Ian.

Paavo Helde

unread,
May 15, 2019, 1:39:56 AM5/15/19
to
On 15.05.2019 1:09, Alf P. Steinbach wrote:
> On 14.05.2019 19:52, Paavo Helde wrote:
>> On 14.05.2019 16:56, Thiago Adams wrote:
>>>
>>> C++ is always moving into a directing of removing the need for
>>> preprocessor.
>>> Is it still necessary? Where do you need/use macros?
>>>
>>> I am not considering #ifdef, #error etc, just macro expansion.
>>
>> 1. For __FILE__, __LINE__:
>>
>> #define MY_ASSERT(x) \
>> if (!(x)) throw AssertException(__FILE__, __LINE__, #x);
>>
>> MY_ASSERT(n>0);
>
> Consider using a macro to generate an instance of a class like <url:
> https://en.cppreference.com/w/cpp/experimental/source_location>, and
> pass that to the exception constructor. It centralizes the handling of
> source location information. More DRY, Don't Repeat Yourself.
>
> Maybe it will be part of C++23?
>
> Of course, with that class' magic one will be able to ditch the macro.

Looking forward to it!

>
>> 2. For avoiding forgetting to define a mutex lock variable:
>>
>> #define BOOST_MUTEX_SCOPED_LOCK(mx) \
>> boost::mutex::scoped_lock boost_mutex_lock(mx)
>>
>> boost::mutex mx1;
>> BOOST_MUTEX_SCOPED_LOCK(mx1);
>>
>> // instead of non-functional
>> // boost::mutex::scoped_lock(mx1);
>
> Instead of one specific macro for each kind of thing one wants to have
> constructor and destructor execution of, I suggest using just one or two
> general macros.

Mutex locks are the only place where I have ever needed this kind of
macro, so for me there is no need for generalization. YMMV, of course.

>
> C# and Java have this "with... some object"-concept as core language
> features.
>
> E.g. write
>
> PH_WITH(boost::mutex::scoped_lock(mx)) {
> // Code here
> }

IIRC in C# the "with" construct is the poor man's substitute for RAII.
In C++ we have real RAII.

If we are speaking about language extensions, then I would rather see an
extension like [[must_have_scope]] which could be attached to the lock
class definition and would prohibit using it in such a way that there is
no other code executed between its ctor and dtor.

>
> Can be defined in C++17 like
>
> /// \brief Binds the specified declarator to `auto&& _` in the following
> braces block.
> ///
> /// `$with` can be used to make clear that an object is used for
> constructor + destructor
> /// execution (RAII), or that it's used just to hold a temporary result
> in order to access
> /// parts of it using the `_` name.
>
> #define PH_WITH( ... ) \
> if ( auto&& _ [[maybe_unused]] = __VA_ARGS__; true )
>
> /// \brief Binds the specified declarator to `const auto& _` in the
> following braces block.
> ///
> #define PH_WITH_CONST( ... ) \
> if( const auto& _ [[maybe_unused]] = __VA_ARGS__; true )

This would not get rid of the macro...
Yes, lambdas might be a cleaner solution. Using decltype to rediscover
the type seems interesting. And with lambdas I believe many usages can
be put inline in the lambda so that I do not need to define artificial
helper functions with cumbersome names, and can replace a lot of
parameter passing boilerplate with the automatic lambda capture.

Thanks for suggestions!

Paavo Helde

unread,
May 15, 2019, 1:55:43 AM5/15/19
to
Agreed the macro approach is ugly.

> (Each invocation would presumably repeat all this code at each
> invocation point. But if it's only used once, then why use the macro.

No, the macro is defined once, and used ... let me grep ... 184 times.
Maybe you missed that the macro can call an arbitrary template function
with arbitrary number of arguments.

There are a couple of other similar macros like DISPATCH_INTEGER and
DISPATCH_FLOATING, but these are also defined once and used many times.

> However, there is also a clear pattern here, and there must be a better
> way of doing it (whatever it is)).

There probably is a better way. Alf's suggestions about lambdas look
promising. These macros predate lambdas about 10 years so maybe it's the
time for a facelift indeed.

Thiago Adams

unread,
May 15, 2019, 7:37:09 AM5/15/19
to
On Tuesday, May 14, 2019 at 7:38:19 PM UTC-3, Bart wrote:
> On 14/05/2019 23:09, Alf P. Steinbach wrote:
> > On 14.05.2019 19:52, Paavo Helde wrote:
> >> On 14.05.2019 16:56, Thiago Adams wrote:
> >>>
> >>> C++ is always moving into a directing of removing the need for
> >>> preprocessor.
> >>> Is it still necessary? Where do you need/use macros?
> >>>
> >>> I am not considering #ifdef, #error etc, just macro expansion.
> >>
> >> 1. For __FILE__, __LINE__:
> >>
> >> #define MY_ASSERT(x) \
> >>     if (!(x)) throw AssertException(__FILE__, __LINE__, #x);
> >>
> >> MY_ASSERT(n>0);
> >
> > Consider using a macro to generate an instance of a class like <url:
> > https://en.cppreference.com/w/cpp/experimental/source_location>, and
> > pass that to the exception constructor. It centralizes the handling of
> > source location information. More DRY, Don't Repeat Yourself.
> >
> > Maybe it will be part of C++23?
>
> Perhaps I'm missing something but, if you're going to build that into a
> language, why do it the hard way?
>
> I implement such things (not in C or C++), as:
>
> f($filename, $lineno)
>
> where these special names are known to the compiler and get substituted
> for the actual module name, and actual source line number. No special
> macros, no special classes, all very easy.

For C and C++ the preprocessor needs to be more integrated with
the compiler and then __FILE__ and __LINE__ could be especial
identifiers just like __func__.

Because compiler and preprocessor are separated you can imagine that
the compiler is not seen files and lines anymore, just tokens.
For __func__ this works but for __FILE__ and __LINE__ not.

I will just add this as another reason why I think preprocessor
should be integrated with the compiler.


I read about std::experimental::source_location but is not clear
how it works. I think they should proposed a basic feature that
allow this class to exist.


Öö Tiib

unread,
May 15, 2019, 10:32:01 AM5/15/19
to
Macros go nowhere until reflection is only possible with preprocessor.
We will need to print variable's name, enumerator's name, function's
name or the whole expression or the source code file path as readable
text at places. That is useful feature and only preprocessor provides
that feature.

The features of preprocessor are better to read used through macros.
Otherwise our code can contain number of repetitive things like:

fprintf(stderr, "%s - Internal error: "
"negative string length %d"
"at %s, line %d.",
timetext(), length,
__FILE__, __LINE__);

The parts of that repetitive garbage that are macros (like __FILE__
and __LINE__) can only be reduced out with macros. I mean that
most of us want to have bloat-free interface like:

report_internal_error("negative string length %d", length);

Also most of us want it to print rest of stuff too like the fprintf did
above. So we have to have report_internal_error as a macro.



Bart

unread,
May 15, 2019, 12:19:18 PM5/15/19
to
void report_internal_error(char* message, int length,
char* file=__FILE__,
int lineno=__LINENO__) {
fprintf(stderr, "%s - Internal error on line %d in %s\n",
timetext(), lineno, file);
fprintf(stderr, message, length);
fprintf(stderr,"\n");
}

Used as:

report_internal_error("negative string length %d", length);

No extra macros needed beyond __LINE__ and __FILE__, and these are easy
candidates to become special compiler variables (they are predefined
macros so are already special).

But it does need:

- Default parameter values

- For this example to work, the expansion of the __LINE__ etc must be
done at the call-site (mentioned this is in a previous post).

So looking ahead to more useful alternatives, rather than building more
and more stuff that absolutely needs to use macros.

Öö Tiib

unread,
May 15, 2019, 1:11:50 PM5/15/19
to
But that does not work with anything.

> No extra macros needed beyond __LINE__ and __FILE__, and these are easy
> candidates to become special compiler variables (they are predefined
> macros so are already special).
>
> But it does need:
>
> - Default parameter values
>
> - For this example to work, the expansion of the __LINE__ etc must be
> done at the call-site (mentioned this is in a previous post).

C preprocessor does not have such concept of "call site". For it most
of our program code is just random pile of text from what it searches
its directives and macros and then rearranges, alters, inserts and
erases it based on those directives and macros.

If you have some wiser preprocessor, then paste its link in github
here and let's have a look. ;)

> So looking ahead to more useful alternatives, rather than building more
> and more stuff that absolutely needs to use macros.

You are joking? The example I did choose was what people have used
last 30 years at least but the function you posted just does work with
nothing that I know of.

Bart

unread,
May 15, 2019, 2:14:18 PM5/15/19
to
On 15/05/2019 18:11, Öö Tiib wrote:
> On Wednesday, 15 May 2019 19:19:18 UTC+3, Bart wrote:

>> void report_internal_error(char* message, int length,
>> char* file=__FILE__,
>> int lineno=__LINENO__) {
>> fprintf(stderr, "%s - Internal error on line %d in %s\n",
>> timetext(), lineno, file);
>> fprintf(stderr, message, length);
>> fprintf(stderr,"\n");
>> }
>>
>> Used as:
>>
>> report_internal_error("negative string length %d", length);
>
> But that does not work with anything.

>> So looking ahead to more useful alternatives, rather than building more
>> and more stuff that absolutely needs to use macros.
>
> You are joking? The example I did choose was what people have used
> last 30 years at least but the function you posted just does work with
> nothing that I know of.

I thought that (1) the C++ group was more receptive to new ideas; (2)
that part of the purpose of the thread was how existing macro usage
could be replaced by something new.

What I proposed could be done with new additions, and in fact Alf
already posted a link to something that can added to a future C++ to
help with this particular requirement.

However, I can write the above today in one of my languages (here using
C's printf to keep it more familiar; in this language access to C's
stderr is tricky):

proc reporterror(ichar mess, int param, lineno=$lineno,
ichar modulename=$modulename) =
printf("Internal error on line %d in module %s:\n ",
lineno, modulename)
printf(mess, param)
printf("\n")
end

And called like this, these calls being on lines 15, 16 and 17 of my
test program 't.ext':

reporterror("Error1 %d",813)
reporterror("Error2 %d",4909)
reporterror("Error3 %d",75)

Output is this:

Internal error on line 15 in module t:
Error1 813
Internal error on line 16 in module t:
Error2 4909
Internal error on line 17 in module t:
Error3 75

(I had to tweak the processing of '$lineno' so that it is expanded
later, and takes on the location of the invoking code.)

So it's not like this stuff is impossible. My implementation doesn't
involve macros, and I can write it now.

Paavo Helde

unread,
May 15, 2019, 2:55:44 PM5/15/19
to
On 15.05.2019 21:14, Bart wrote:
>> However, I can write the above today in one of my languages (here using
> C's printf to keep it more familiar; in this language access to C's
> stderr is tricky):
>
> proc reporterror(ichar mess, int param, lineno=$lineno,
> ichar modulename=$modulename) =
> printf("Internal error on line %d in module %s:\n ",
> lineno, modulename)
> printf(mess, param)
> printf("\n")
> end
>

It appears in your language there are no semicolons between the
statements. I wonder how do the assignments look like?

Thiago Adams

unread,
May 15, 2019, 3:11:06 PM5/15/19
to
On Wednesday, May 15, 2019 at 3:14:18 PM UTC-3, Bart wrote:
> On 15/05/2019 18:11, Öö Tiib wrote:
> > On Wednesday, 15 May 2019 19:19:18 UTC+3, Bart wrote:
>
> >> void report_internal_error(char* message, int length,
> >> char* file=__FILE__,
> >> int lineno=__LINENO__) {
> >> fprintf(stderr, "%s - Internal error on line %d in %s\n",
> >> timetext(), lineno, file);
> >> fprintf(stderr, message, length);
> >> fprintf(stderr,"\n");
> >> }

C#, that doesn't have a separated preprocessor (it is not like
C preprocessor) ant it doesn't support macro expansion.
They found a solution for __LINE__ and __FILE__.

Caller Information has been added to .NET 4.5
https://stackoverflow.com/questions/696218/do-line-file-equivalents-exist-in-c

It is an attribute:

public void Log(string message,
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
// Do logging
}

I didn't like this approach but it is interesting to see
alternatives.


Rust has:
https://doc.rust-lang.org/std/macro.file.html

I don't know how macros works in Rust but they are probably different
from C.


In my opinion it should be __line__ and __file__ just like
__func__ and compilers do what is necessary to implement it.

Bart

unread,
May 15, 2019, 3:18:44 PM5/15/19
to
Plenty of languages have learnt how to do without semicolons. Which
makes sense since even 'free-format' language syntax is predominantly
written in a line-oriented format.

Here however the semicolons are still needed to separate statements.
It's just that end-of-line is interpreted as a semicolon (save when the
line obviously continues onto the next, eg. after "("). Extra semicolons
are also harmless.

(This is a more extensive program in that syntax (first assignment on
line 91):

https://github.com/sal55/qx/blob/master/lisp.m

Zero semicolons in 2000 lines, except inside string constants. There are
also zero macros.

Note this is not working code; it's been auto-converted from C as an
experiment in visualisation. The difference from the original C is striking:

https://github.com/sal55/qx/blob/master/lisp.c


Öö Tiib

unread,
May 16, 2019, 11:48:34 AM5/16/19
to
On Wednesday, 15 May 2019 21:14:18 UTC+3, Bart wrote:
> On 15/05/2019 18:11, Öö Tiib wrote:
> > On Wednesday, 15 May 2019 19:19:18 UTC+3, Bart wrote:
>
> >> void report_internal_error(char* message, int length,
> >> char* file=__FILE__,
> >> int lineno=__LINENO__) {
> >> fprintf(stderr, "%s - Internal error on line %d in %s\n",
> >> timetext(), lineno, file);
> >> fprintf(stderr, message, length);
> >> fprintf(stderr,"\n");
> >> }
> >>
> >> Used as:
> >>
> >> report_internal_error("negative string length %d", length);
> >
> > But that does not work with anything.
>
> >> So looking ahead to more useful alternatives, rather than building more
> >> and more stuff that absolutely needs to use macros.
> >
> > You are joking? The example I did choose was what people have used
> > last 30 years at least but the function you posted just does work with
> > nothing that I know of.
>
> I thought that (1) the C++ group was more receptive to new ideas; (2)
> that part of the purpose of the thread was how existing macro usage
> could be replaced by something new.
>
> What I proposed could be done with new additions, and in fact Alf
> already posted a link to something that can added to a future C++ to
> help with this particular requirement.

I wrote:

! Macros go nowhere until reflection is only possible with preprocessor.

Obviously if we add non-preprocessor reflection to C++ then
macros go away. It is awful idea to use that preprocessorish syntax for
non-preprocessor reflection.

And in next message I also wrote that you erased:

! C preprocessor does not have such concept of "call site". For it most
! of our program code is just random pile of text from what it searches
! its directives and macros and then rearranges, alters, inserts and
! erases it based on those directives and macros.
!
! If you have some wiser preprocessor, then paste its link in github
! here and let's have a look. ;)

Since what you have is ?maybe? some concept of more context-aware
preprocessor but then you can't just propose it with random one-liners.
Even existing art (the extensions in actual compilers) won't get into
standard too easily.

> However, I can write the above today in one of my languages (here using
> C's printf to keep it more familiar; in this language access to C's
> stderr is tricky):
>
> proc reporterror(ichar mess, int param, lineno=$lineno,
> ichar modulename=$modulename) =
> printf("Internal error on line %d in module %s:\n ",
> lineno, modulename)
> printf(mess, param)
> printf("\n")
> end

Here these are apparently not preprocessor macros but
reflection that is part of language.

Thiago Adams

unread,
May 16, 2019, 1:36:47 PM5/16/19
to
On Tuesday, May 14, 2019 at 2:52:16 PM UTC-3, Paavo Helde wrote:
...
> 4. For compiler-specific stuff.
>
> #ifdef _MSC_VER
> #define DI_FOO __declspec(dllexport)
> #else
> #define DI_FOO
> #endif
>
> #if __GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=7)
> #define OVERRIDE override
> #else
> #define OVERRIDE
> #endif


If we had just #ifdef without macro expansion (like c#)
we could do something like this:


class X
{
virtual void Func()
#if HAS_OVERRIDE
override;
#endif
};


class X
{
#if HAS_OVERRIDE
virtual void Func() override;
#else
virtual void Func();
#endif
};

Not so good but it works.








Paavo Helde

unread,
May 16, 2019, 2:25:12 PM5/16/19
to
On 16.05.2019 20:36, Thiago Adams wrote:
> On Tuesday, May 14, 2019 at 2:52:16 PM UTC-3, Paavo Helde wrote:
> ...
>> 4. For compiler-specific stuff.
>>
>> #ifdef _MSC_VER
>> #define DI_FOO __declspec(dllexport)
>> #else
>> #define DI_FOO
>> #endif
>>
>> #if __GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=7)
>> #define OVERRIDE override
>> #else
>> #define OVERRIDE
>> #endif
>
>
> If we had just #ifdef without macro expansion (like c#)
> we could do something like this:
>
>
> class X
> {
> virtual void Func()
> #if HAS_OVERRIDE
> override;
> #endif
> };

And make each declaration 4 lines instead of 1? No thanks.

>
> class X
> {
> #if HAS_OVERRIDE
> virtual void Func() override;
> #else
> virtual void Func();
> #endif
> };
>
> Not so good but it works.

This is even worse and does not actually work.

The override specifier is needed when I change the base class function
declaration and forget to change it in all derived classes. With
OVERRIDE macro I can catch this error on the platform/compiler which
supports override, and I can fix the problem for all compilers/platforms.

Alas, with this duplicate copy-pasta approach I am forced to fix only
one of the declarations and may leave the other one broken. IOW, the
whole point of having 'override' is lost.


Bart

unread,
May 16, 2019, 3:48:53 PM5/16/19
to
On 16/05/2019 16:48, Öö Tiib wrote:
> On Wednesday, 15 May 2019 21:14:18 UTC+3, Bart wrote:

> I wrote:
>
> ! Macros go nowhere until reflection is only possible with preprocessor.
>
> Obviously if we add non-preprocessor reflection to C++ then
> macros go away. It is awful idea to use that preprocessorish syntax for
> non-preprocessor reflection.
>
> And in next message I also wrote that you erased:
>
> ! C preprocessor does not have such concept of "call site". For it most
> ! of our program code is just random pile of text from what it searches
> ! its directives and macros and then rearranges, alters, inserts and
> ! erases it based on those directives and macros.
> !
> ! If you have some wiser preprocessor, then paste its link in github
> ! here and let's have a look. ;)
>
> Since what you have is ?maybe? some concept of more context-aware
> preprocessor but then you can't just propose it with random one-liners.

Why not? You start with what you want to achieve, and how you want to
write it.

I don't limit myself to what's possible with a preprocessor. Because
with my stuff I can change the language and compiler.

I understand the OP is working on some source-to-source converter which
can do a lot more than a dumb preprocessor.

The C preprocessor is limited because it doesn't know anything about the
syntax of the program, or its scoped symbol tables, or its types. It
might not even know what language it's being used on (as it can be
applied to other files than C or C++ source code).

> Here these are apparently not preprocessor macros but
> reflection that is part of language.

Actually __LINE__ etc are also kind of built-in. They certainly are not
like ordinary macros you create with #define.

But they are still part of the preprocessor so have the same issues.

Robert Wessel

unread,
May 16, 2019, 5:10:06 PM5/16/19
to
Does using macros to generate templates count?

A library I'm working on needs to generate large number of templates
to cover operator overloads. In many cases this is a block of a dozen
lines of code that's repeated a dozen times, with just the type of
operation and return type varying. I'm not convinced I'm going to
leave it like this, but there's a big chunk of replicated code here.
There are similar macros for assignment operators, and for some of the
other types.



#define __BIGSCALED_CONVERSION_TEMPLATES_BINOPS(rtype, opername) \
rtype opername(const bigscaled &op2) const \
{ return tobigrational().opername(op2.tobigrational()); } \
friend rtype opername(const bigscaled &op1, const bigrational &op2)
\
{ return op1.tobigrational().opername(op2); } \
friend rtype opername(const bigrational &op1, const bigscaled &op2)
\
{ return op1.opername(op2.tobigrational()); } \
template<typename OP1> \
friend rtype opername(const OP1 op1, const bigscaled &op2) \
{ return bigrational(op1).opername(op2.tobigrational()); }
\
template<typename OP2> \
friend rtype opername(const bigscaled &op1, const OP2 op2) \
{ return op1.tobigrational().opername(bigrational(op2)); }



// Binary operators
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bigrational, operator+)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bigrational, operator-)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bigrational, operator*)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bigrational, operator/)
//operator%???
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator==)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator!=)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator>)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator>=)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator<)
__BIGSCALED_CONVERSION_TEMPLATES_BINOPS(bool, operator<=)

Öö Tiib

unread,
May 17, 2019, 9:42:32 AM5/17/19
to
On Thursday, 16 May 2019 22:48:53 UTC+3, Bart wrote:
> On 16/05/2019 16:48, Öö Tiib wrote:
> > On Wednesday, 15 May 2019 21:14:18 UTC+3, Bart wrote:
>
> > I wrote:
> >
> > ! Macros go nowhere until reflection is only possible with preprocessor.
> >
> > Obviously if we add non-preprocessor reflection to C++ then
> > macros go away. It is awful idea to use that preprocessorish syntax for
> > non-preprocessor reflection.
> >
> > And in next message I also wrote that you erased:
> >
> > ! C preprocessor does not have such concept of "call site". For it most
> > ! of our program code is just random pile of text from what it searches
> > ! its directives and macros and then rearranges, alters, inserts and
> > ! erases it based on those directives and macros.
> > !
> > ! If you have some wiser preprocessor, then paste its link in github
> > ! here and let's have a look. ;)
> >
> > Since what you have is ?maybe? some concept of more context-aware
> > preprocessor but then you can't just propose it with random one-liners.
>
> Why not? You start with what you want to achieve, and how you want to
> write it.

Because the fact that it was feature proposal did remain dim for me.
It is maybe my issue. But the way of implementing it was silent change
of behavior. There I am certain that snowballs in lava are more likely
than either committee accepting such obvious silent change of behavior
into C or C++ language. Backwards compatibility is one of most important
design goals of those languages.

> I don't limit myself to what's possible with a preprocessor. Because
> with my stuff I can change the language and compiler.
>
> I understand the OP is working on some source-to-source converter which
> can do a lot more than a dumb preprocessor.

These are orthogonal things: other tools of processing possibly (C and/or)
C++ like texts and suggestions to change (C and/or) C++ programming
languages. Please try to express these explicitly separated ... otherwise
it is hard to realise which it is you are talking about. I'm interested
in both topics but not in mix.

> The C preprocessor is limited because it doesn't know anything about the
> syntax of the program, or its scoped symbol tables, or its types. It
> might not even know what language it's being used on (as it can be
> applied to other files than C or C++ source code).

I fully agree with you.

> > Here these are apparently not preprocessor macros but
> > reflection that is part of language.
>
> Actually __LINE__ etc are also kind of built-in. They certainly are not
> like ordinary macros you create with #define.

Of course. Every feature of preprocessor is built into preprocessor.
These to are preprocessor functions that are called thru macro-like
syntax. The values that __LINE__ and __FILE__ return can be updated
(by user) using #line preprocessor directive:

#line 42 "bullshit.py"

> But they are still part of the preprocessor so have the same issues.

Right. Either we have those as part of C++ language (with different
syntax than now) or we are doomed to use preprocessor forever.

Sal LO

unread,
May 19, 2019, 5:59:07 PM5/19/19
to
0 new messages