1

I have two lists:

#define LIST1 {1, 2, 3}
#define LIST2 {4, 5, 6}

and using C++ macros I would like to write something like this:

// Obviously doesn't work
#define MERGE LIST1 ## LIST2
int my_array[] = MERGE;

to yield:

int my_array[] = {1, 2, 3, 4, 5, 6};

during compile-time.

Is something like this possible? There are other questions concerning this with strings, however I can't figure out for the life of me how to do this with non-string array declarations.

Edit: I certainly would prefer to not use macros, and would prefer that the list format be different as well. Unfortunately, the header file that contains these list definitions isn’t a file that I can edit.

keenick4
  • 33
  • 7
  • General recommendations typically go in the direction of avoiding macros whenever possible; so, first off, do LIST1 and LIST2 really _need_ to be preccompiler macros? If they don't, which I suspect, this problem becomes much easier (when e.g. using std::vector constants...) – codeling Dec 20 '21 at 08:03
  • In this case, these lists hold pointer references to peripheral registers for embedded hardware (CMSIS). I agree that a macro isn't ideal, but it would let me be a lot more general with my code as these lists automatically change depending on the compile target. – keenick4 Dec 20 '21 at 08:09
  • Ok I missed the bit about pointer references, arrays can't hold references so you probably want to store actual pointers. (https://stackoverflow.com/questions/1164266/why-are-arrays-of-references-illegal) – Pepijn Kramer Dec 20 '21 at 08:19
  • might want to add this information to the question, otherwise, you'd just get "don't use macros" answers... – codeling Dec 20 '21 at 10:07

2 Answers2

4

Don't use macros unless there is no other option, prefer templates. They are typesafe.

For example you can make a compile time evaluated function (constexpr) that merges two lists (arrays) and returns an array.

#include <array>

// type_t is the type held by the array (an int in this example)
// N = size of first array
// M = size of second array
// const type_t(&arr)[N] is the syntax for passing an array by const reference

template<typename type_t, std::size_t N, std::size_t M>
constexpr auto merge(const type_t(&arr1)[N], const type_t(&arr2)[M])
{
    std::array<type_t, N + M> arr{}; // this initialization is needed in constexpr
    std::size_t index{ 0 };

    for (const auto& value : arr1) arr[index++] = value;
    for (const auto& value : arr2) arr[index++] = value;

    return arr;
}

int main()
{
    constexpr auto arr = merge({ 1,2,3 }, { 4,5,6 });
    constexpr auto strings = merge( {"abc", "def" }, {"ijk", "lmn"} );

    // static_assert is like assert, but evaluated at compile time.
    static_assert(arr.size() == 6);
    static_assert(arr[4] == 5);

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • 2
    Sometimes I am disappointed by looking for an answer to a "bad practice question" and only find "don't do it" answers. This answer serves both, thats nice. If OPs wants to use their macros (for whatever reasons) they can still do it. – 463035818_is_not_an_ai Dec 20 '21 at 08:52
  • Been some time since I used cpp, but oh man looking at the syntax for templates (or rather the things you need to do right to get it right) I can understand why some people still prefer macros, if I told my physics prof to do this instead of his 100s of macros way back then he would kick me out :p – arynaq Dec 20 '21 at 08:52
  • @arynaq with some time you get used to it and this code is actually rather nice and readable. On the other hand, after many years of using macros I still get confused by them and they introduce nasty bugs in my code – 463035818_is_not_an_ai Dec 20 '21 at 08:54
  • @463035818_is_not_a_number Yeah I have the same with macros. Thing is with templates the compiler actually helps finding out what's wrong. – Pepijn Kramer Dec 20 '21 at 09:00
  • @arynaq Must have been quite a while then :) . Templates have been around since at least 1995 (yes I was there). They only got better since. And like most programming languages start with using features and they will start to feel pretty natural after a while. – Pepijn Kramer Dec 20 '21 at 09:07
  • [`std::copy`](https://en.cppreference.com/w/cpp/algorithm/copy) is `constexpr` since C++20, to replace the manual loop. – Jarod42 Dec 20 '21 at 09:41
  • Need other implementation for non-default constructible types though (as using `std::index_sequence` to construct array directly: `return {{ arr1[Is1]..., arr2[Is2]... }}`). – Jarod42 Dec 20 '21 at 09:43
  • @jarod42 copy constexpr I still am in two minds about posting c++20 examples... Seems it is not in widespread use still. But that's another discussion. Thanks for the index sequence made me learn something too – Pepijn Kramer Dec 20 '21 at 09:52
4

It is easily achievable by slightly restating your code:

#define LIST1 1, 2, 3
#define LIST2 4, 5, 6
#define MERGE LIST1, LIST2
int my_array[] = { MERGE };

#include <iostream>
int main() {
  for(auto const& x : my_array) {
    std::cout << x << "\n";
  }
}

Demo

danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • Still macros are not recommended, more info here : https://stackoverflow.com/questions/14041453/why-are-preprocessor-macros-evil-and-what-are-the-alternatives – Pepijn Kramer Dec 20 '21 at 08:17
  • We get it, you don't like macros. I, myself, would rarely resort to C-style macros if I can avoid their usage. That does not change, that this answers the question. – danielschemmel Dec 20 '21 at 08:21
  • True it answers it, that's why I did not downvote ;) I just want to teach better C++ – Pepijn Kramer Dec 20 '21 at 08:26