0

I have a struct like this:

typedef struct {
  int num_files;
  audio_file files[];
} message_files;

and I want to create a macro so I don't have to specify the number of files of a statically known array:

#define FILES(arr) {sizeof((audio_file []) arr) / sizeof(audio_file), arr}

message_files files[3] = {
  FILES({{1,500}, {2,500}}),
  FILES({{3,1000}),
  FILES({{4,5000}, {5,10}})
};

But this doesn't work: I get error: macro "FILES" passed 4 arguments, but takes just 1. How can I make the macro accept the array literal as such?

9
  • {{1,500}, {2,500}} is not an array literal. What are you trying to do here? Assuming the error in question is fixed, macro expansion would lead to message_files files[1] = { {sizeof((audio_file []) {{1,500}, {2,500}}) / sizeof(audio_file), {{1,500}, {2,500}}} }; which doesn't make sense syntactically. Commented Oct 15, 2024 at 10:40
  • 5
    It doesn't make sense conceptually to form an array of message_files. This is a class with flexible array member (which btw. are not valid in standard C++), so it should be individually allocated for. (audio_file []) arr is also not valid C++ syntax. Compound literals exist in C, but not C++. Commented Oct 15, 2024 at 10:40
  • I'm programming for Arduino, this may explain the C/C++ mixture. Commented Oct 15, 2024 at 10:44
  • 1
    @returntrue It is not clear what you are trying to achieve, even if I take the non-standard extensions from C for granted. Flexible array members must be used differently with dynamic allocation. Commented Oct 15, 2024 at 10:45
  • 1
    Why bother with macros instead of templated reference to array and variadic templates if C++ is used anyway? What C++ version are you on? Commented Oct 15, 2024 at 11:05

3 Answers 3

3

Your code is decidedly a "C" style approach, in C++20 I would do this :

#include <array>
#include <string_view>
#include <concepts>

struct audio_file_t
{
    int id;
    std::string_view name;
};

int main()
{
    constexpr auto files = std::to_array<audio_file_t>({{ 1, "file1" }, { 2, "file2" }});
    static_assert(files.size() == 2);
}

Update to include a more nested structure (no longer constexpr)

#include <array>
#include <vector>
#include <string_view>
#include <iostream>
#include <format>

struct audio_file_t
{
    int id;
    std::string_view name;
};

struct message_files_t
{
    int id;
    std::vector<audio_file_t> files;
};

std::ostream& operator<<(std::ostream& os, const audio_file_t& file)
{
    std::cout << std::format("File id : {}, name : {}", file.id, file.name);
    return os;
}

int main()
{
    constexpr auto files = std::to_array<audio_file_t>({{ 1, "file1" }, { 2, "file2" }});
    static_assert(files.size() == 2);

    std::vector<audio_file_t> temp{{ 4, "file3.1" }, { 5, "file3.1" }, { 6, "file3.1" }};

    

    // This is the initialization you're looking for
    std::vector<message_files_t> multi_files{ 
        {11, {{ 1, "file1.1" }, { 2, "file2.1" }}},
        {22, {{ 3, "file2" }}},
        {33, {{ 4, "file3.1" }, { 5, "file3.2" }, { 6, "file3.3" }}}
    };
    
    for(const auto& message_files : multi_files)
    {
        std::cout << "Message Files, id = " << message_files.id << "\n";
        for( const auto& audio_file : message_files.files)
        {
            std::cout << audio_file << "\n";
        }
    }

}
Sign up to request clarification or add additional context in comments.

3 Comments

How does this work when files consists of more than one message_files?
I edited the question to clarify what I want to achieve.
I updated my answer whatever you do don't use macros
0

If you need to pass an argument that contains , to a C or C++ macro, you generally need to ensure it's enclosed in at least one level of parentheses (braces don't work). For example:

message_files files[3] = {
    FILES(({{1,500}, {2,500}})),
    FILES(({{3,1000}})),
    FILES(({{4,5000}, {5,10}}))
};

That said, a macro is almost certainly the wrong approach here, and a template function would be much better.

Comments

-2

I found a solution using va_args:

#define FILES(...) (message_files) { (sizeof((audio_file []) {__VA_ARGS__}) / sizeof(audio_file)), (audio_file[]) { __VA_ARGS__ }}

3 Comments

Proposing a "C" style macro with VA_ARGS no less is not really a C++ approach
It doesn't work though. audio_files doesn't appear in your question and as I said in my comments, you are fundamentally using flexible array members wrong. Any compiler that supports flexible array members that I know will complain about it. You can't initialize them with a braced-init-list. Instead they are supposed to cover memory after the structure in an overallocation for the structure via malloc.
Oh it does work. I didn't ask for a C++-idiomatic solution, just something that works for my Arduino project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.