1

I need to do a loop where I turn this simple example of reflection:

std::string mystring[3] = {{"mystring[0]"},{"mystring[1]"},{"mystring[2]"}};

into a more managable form for longer arrays. The solution sounds like I should either use macros with a loop, or recursion. However, macros don't support loops or recursion!

How do I create a macro to handle this for me arbitrarily?

#define NAME_OBJ(type, name, size)
Stewart
  • 4,356
  • 2
  • 27
  • 59
  • 2
    Avoid macros whenever possible. Use proper functions when you can. – Jesper Juhl Apr 06 '18 at 09:06
  • what you want? what's wrong with a function return a vector? – apple apple Apr 06 '18 at 09:13
  • How is it an " example of reflection"? – Cheers and hth. - Alf Apr 06 '18 at 09:15
  • After a ton of searching, I mostly wrote this question using the Q&A format of stack overflow so that I can find the answer again later. Yeah, I know that macros should be avoided where possible, but they are essential when it comes to reflection. This is simple code, but in reality, std::string is actually a structure with reflection around it which requires the literal name of the instance. – Stewart Apr 06 '18 at 09:23
  • 1
    @Stewart I'm currious as to why would consider `std::string` to be using "reflection" since C++ does not have that feature..? And where do macros come in as regards to "reflection"? – Jesper Juhl Apr 06 '18 at 09:29
  • Perhaps I'm using the word wrong, but I don't think so. "Reflection" is the concept of bringing in run-time-type-information. std::string itself isn't reflection, but you can embed meta-information about your object in it. You're right that the language doesn't support it, but we can use the pre-processor to get around that... Take this macro: `#define STRUCT(name, body) struct name { std::string m_body; body name() : m_body(#body){} };`. This struct will use the second argument as a C++ body, and will also save it to a string in the member, allowing you to parse it during runtime. – Stewart Apr 09 '18 at 13:34
  • That's a super-simple example, but you can imagine how you can start embedding sizes and type names as well. BOOST_HANA uses macros in this way quite a bit. – Stewart Apr 09 '18 at 13:35

3 Answers3

5

Try using Boost. If it fails, try using more Boost.

#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/stringize.hpp>

#define DETAIL_NAME_OBJ_ELEM(z, n, name) \
    BOOST_PP_COMMA_IF(n) { BOOST_PP_STRINGIZE(name) "[" BOOST_PP_STRINGIZE(n) "]" }

#define NAME_OBJ(type, name, size) \
    type name[size] = { BOOST_PP_REPEAT(size, DETAIL_NAME_OBJ_ELEM, name) }

Then this:

NAME_OBJ(std::string, mystring, 3);

... expands to this:

std::string mystring[3] = {
    { "mystring" "[" "0" "]" },
    { "mystring" "[" "1" "]" },
    { "mystring" "[" "2" "]" }
};

... in which the adjacent string literals are then automatically merged before compilation.

See it live on Coliru

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Worth noting that "using more boost" is likely to severely hurt your compile times. The boost headers are notorious for dragging in a shit ton of other boost headers and other crap (even when they don't really need to and could just include something more specific). I've shaved *minutes* off of a real-life projects compile time by simply a) replace boost headers with std equivalents and b) remove unneeded boost header includes and/or include more targeted boost includes. – Jesper Juhl Apr 06 '18 at 09:18
  • 2
    Anonymous downvoter, you're demonstrating that you're unable to articulate you view. When enough people do that it reflects poorly on the site. Consider rational discussion instead. – Cheers and hth. - Alf Apr 06 '18 at 09:20
  • @JesperJuhl fair warning, although it does depend a lot on what Boost library we're talking about. But this is more of an humorous placeholder instead of "here, you can do this" ;) – Quentin Apr 06 '18 at 09:22
  • @Quentin Indeed. Not all boost libraries are created equal. Just thought I'd mention it since it has bitten me severely in the past. – Jesper Juhl Apr 06 '18 at 09:25
  • Quick question: What's the advantage of `BOOST_PP_STRINGIZE(name)` over `#name`? – Stewart Apr 06 '18 at 09:30
  • 1
    @Stewart `BOOST_PP_STRINGIZE` ensures that substitution happens before stringification (otherwise you stringize the name of the macro that was passed in). It might not be needed here, but since `name` comes through the pipes of `BOOST_PP_REPEAT` I prefer to be safe :) – Quentin Apr 06 '18 at 09:33
0

Based on a great answer to handling macros here https://stackoverflow.com/a/12540675/1723954

I modified/simplified the solution for this specific case:

#define NAME_OBJ(type, name, size) \
    type name[size] = { INIT_ELEMENT_ ## size(name) }

#define INIT_ELEMENT_1(name) { #name "[0]" }
#define INIT_ELEMENT_2(name) INIT_ELEMENT_1(name), { #name "[1]" }
#define INIT_ELEMENT_3(name) INIT_ELEMENT_2(name), { #name "[2]" }
...

The solution does not allow for very-very-very large arrays, only as large as you are willing to code. I did 128 and that's fine for all of the cases in my project.

Stewart
  • 4,356
  • 2
  • 27
  • 59
  • You have a little mistake at `#define INIT_ELEMENT_3(name) INIT_ELEMENT_3(name), { #name "[2]" }`, must be `#define INIT_ELEMENT_3(name) INIT_ELEMENT_2(name), { #name "[2]" }` – LuisGP Apr 06 '18 at 08:58
0

As you stated, macros in C++ do not support loops or recursion, such that you would have to duplicate lines of code.

A common way of solving such topics is to write a program - let's say myGenerator, which contains a "normal" C++-loop and which's output is a C++ source file (.cpp or .h). Integrate then a call to myGenerartor in a build step before building the rest of your program.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58