harunB10
harunB10

Reputation: 5207

Variable initialization in Macro - Multiple initialization

I need to implement some db check using macro.

Lets suppose that we have something like this:

#define DB_CHECK(prop, records)      bool found = false; \
                                     for(auto i : records) \
                                       if(i.id == prop.id) found = true; break; \
                                       if(!found) PRINT_ERROR("Some error");

And in the code I call it like this:

std::list <RecordClass> recordsList;
std::list <AnotherClass> anotherClassList;
for(auto &i : myDBrecords)
{
   DB_CHECK(i, recordsList);
   DB_CHECK(i, anotherClassList);
}

For this I get an error for boolean found initialization.

'found' : redefinition; multiple initialization

Do you have any idea how can I avoid this? Providing another argument for found in macro?

Upvotes: 0

Views: 141

Answers (2)

armagedescu
armagedescu

Reputation: 2158

It is a Compile Error, caused by second call to DB_CHECK macro. It declares "bool found" again in the same scope of visibility. You can solve the problem by enclosing in brackets {}, this will separate the visibility scopes:

#define DB_CHECK(prop, records)      {bool found = false; \
                                     for(auto i : records) \
                                     {if(i.id == prop.id) found = true; break;} \
                                       if(!found) PRINT_ERROR("Some error");}

Or by enclosing the calls

for(auto &i : myDBrecords)
{
   {DB_CHECK(i, recordsList);}
   {DB_CHECK(i, anotherClassList);}
}

Ok, there exists better approach, the template functions, you have much more control over code, variables and flow

template<typename T1, typename T2> void DB_CHECK(T1 prop, T1 records)
{
    for(auto i : records) if(i.id == prop.id) return;
    PRINT_ERROR("Some error");
}

Upvotes: 4

UnforeseenPrincess
UnforeseenPrincess

Reputation: 334

another approach is to use the __COUNTER__ macro to create a unique variable name.

#define TIE_VARIABLE_NAME_IMPL(name, count) name##count
#define TIE_VARIABLE_NAME(name, count) TIE_VARIABLE_NAME_IMPL(name, count)
#define UNIQUE_NAME(name) TIE_VARIABLE_NAME(name, __COUNTER__)

#define DB_CHECK_IMPL(prop, records, unique_found, unique_i)  \
{bool unique_found = false; \
for(auto unique_i : records) { \
    if(unique_i.id == prop.id){ unique_found = true; break;} } \
if(!unique_found) PRINT_ERROR("Some error");}

#define DB_CHECK(prop, records) DB_CHECK_IMPL(prop, records, UNIQUE_NAME(found), UNIQUE_NAME(i))

I learned about this approach from this library here https://github.com/ned14/outcome/blob/develop/include/outcome/try.hpp#L145

In your case, I see the approach mentioned in the other answer as a simpler solution to your issue, but this is an alternative way to solve the problem and may be useful in other macro implementations where, for whatever reason, creating an additional scope is undesirable.

This approach also avoids shadowing warnings (e.g. -Wshadow) in this type of scenario:

bool found = false; // user has a variable named `found` in the scope
DB_CHECK(i, recordsList); // we dont shadow the users `found` because we used a unique name

for more info on the counter macro: https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html also I am not sure how widely supported that macro is, but msvc, clang, and gcc all seem to support it.

Upvotes: 0

Related Questions