2

I posted a similar question yesterday, the site suggested to post a new question with better explanations.

There are two macros:

#define COMPANY L"Test Company"
#define PRODUCT COMPANY L" in Canada"

The result of PRODUCT will be "Test Company in Canada".

Now, we have the following requirements:

  1. make the COMPANY to be "dynamic" string, to call a function to return a company name, e.g . #define COMPANY getCompanyName()
  2. we are not allowed to change the other code to reference the COMPANY, such as #define PRODUCT COMPANY L" in Canada", since there are so many macros in the code

The issue with change: The result of PRODUCT will be "Test Company", lost the part " in Canada" literal.

Here is the code:

#include <stdio.h>
#include <tchar.h>
const wchar_t* getCompanyName() { return L"Test Company";};
#define COMPANY getCompanyName();
#define PRODUCT COMPANY L" in Canada"

int _tmain(int argc, _TCHAR* argv[])
{

const wchar_t * company = COMPANY; // get Test Company
const wchar_t * product = PRODUCT; // get Test Company in Canada

wprintf(company);
wprintf(product);


return 0;
} 
Community
  • 1
  • 1
  • What you ask for is simply not possible, I think. Other macros expect to be able to perform compile-time string concatenations. If changing those other macros is not allowed, the only possible conclusion is that the operands of the concatenation must be compile-time constants (string literals) as well. –  Oct 07 '13 at 11:00
  • If you can't redefine PRODUCT - there is no valid way to do so - compile-time string concatenation requires compile-time constants. Btw, using ';' inside macro is very error-prone. – keltar Oct 07 '13 at 11:01
  • 3
    *"since there are so many marcos in the code"* ~> Then it's time to change the code. – LihO Oct 07 '13 at 11:06
  • @keltar: Yes, compile-time string concatenation requires compile-time constants. But that only means that the macro expansion has to start and end with them, not that it can't contain anything else. – Jan Hudec Oct 07 '13 at 12:13
  • @JanHudec if it contains something else - it's no longer compile-time (and not preprocessor concatenation anyway). There are still some chances that optimiser will catch this case, but i wouldn't count on it – keltar Oct 08 '13 at 03:10
  • @keltar: It's no longer compile-time. The point is that it's syntactically correct both to append or prepend string or not. – Jan Hudec Oct 08 '13 at 08:23

2 Answers2

2

You PRODUCT macro expands in

getCompanyName(); L" in Canada"

so

const wchar_t * product = getCompanyName(); L" in Canada";
wprintf(product);

prints

Test Company

as expected.

In C++ we tend to:

  • avoid macros (use inline functions instead)
  • avoid naked pointers (prefer STL facilities)

So, In C++ we prefer:

inline const std::wstring getCompanyName() { return L"Test Company";}
inline const std::wstring PRODUCT() { return getCompanyName() + L" in Canada";}
Ivan Aksamentov - Drop
  • 12,860
  • 3
  • 34
  • 61
  • 2
    -1: unless I'm very much mistaken (do point it out if that is the case), this wouldn't even compile, so it wouldn't print anything. –  Oct 07 '13 at 11:02
  • sorry, I've forget `product` variable. Indeed, wprintf(PRODUCT) not compiles – Ivan Aksamentov - Drop Oct 07 '13 at 11:05
  • 1
    Not exactly that. First, it's syntax error. Second, OP's macro have semicolon, so it would be `const wchar_t *product = getCompanyName(); L" in Canada";`, with last string constant being no-op so it'll be discarded. – keltar Oct 07 '13 at 11:08
  • is there a way to stringnize the function getCompanyName() so that it will like lateral? – user2852910 Oct 07 '13 at 11:10
  • With your last edit, this looks correct to me, so downvote removed. –  Oct 07 '13 at 11:12
  • @user2852910 isn't it exactly what you've tried to revert in the first place? – keltar Oct 07 '13 at 11:12
  • There is an inconsistency between the definitions. First you say `PRODUCTS` expand to the two expressions without any separator. And using that wouldn't compile anywhere. And than you assign the variable and have semicolon there. That compiles, but does not match the expression above. And if you tried to use it directly, it wouldn't compile as expected. – Jan Hudec Oct 07 '13 at 11:56
  • @JanHudec, yep, second semicolon I've also forgot. Fixed. – Ivan Aksamentov - Drop Oct 07 '13 at 15:46
2

It's a nasty hack, but it's actually possible. Define COMPANY to an expression, that starts with a literal, ends with a literal and can be implicitly converted to a const wchar_t *:

#define COMPANY L"" + getCompanyName() + L""

Of course the getCompanyName() must not return const wchar_t *, because operator+ is not defined for two pointers and it would work on addresses and not strings anyway.

You basically need as std::wstring, but you probably need it to be convertible to const wchar_t *, which std::wstring is not. So you'd have to define your own class:

struct AutoConvertibleString {
    std::string s;
    AutoConvertibleString(const std::string &s) : s(s) {}
    // C++ optimization for move:
    // AutoConvertibleString(std::string s) : s(std::move(s)) {}
    operator const wchar_t *() { return s.c_str(); }
};
AutoConvertibleString operator+(const wchar_t *l, const AutoConvertibleString &r) {
    return (l + r.s).c_str();
}
AutoConvertibleString operator+(const AutoConvertibleString &l, const wchar_t *r) {
    return (l.s + r).c_str();
}
// Ok, the operator+s could be optimized to use move for temporaries too...

AutoConvertibleString getCompanyName() { /* ... whatever ... */ }

It's an ugly hack. It would really be better to convert all of them to functions. But it should work.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • thank you so much for your time, I love the answer. It is so close now. There is a compilation error, I will try to fix it. error C2668: 'AutoConvertibleString::AutoConvertibleString' : ambiguous call to overloaded function 1> c:\dev\dotnet\strtest\strtest\strtest.cpp(10): could be 'AutoConvertibleString::AutoConvertibleString(std::wstring)' 1> c:\dev\dotnet\strtest\strtest\strtest.cpp(9): or 'AutoConvertibleString::AutoConvertibleString(const std::wstring &)' – user2852910 Oct 07 '13 at 12:39
  • @user2852910: You should only have one of those lines depending on your C++ version, never both. I'll just comment one out. – Jan Hudec Oct 07 '13 at 12:43
  • yes, I did it and it compiled - some how the string content is messed - I will do more debug. – user2852910 Oct 07 '13 at 12:47