Home > Back-end >  How to embed the data into array in compile time in C ?
How to embed the data into array in compile time in C ?

Time:05-01

I want to load a database into an array at compile time like:

//a.dat
1 2 3 4 5
int main(){ 
    unsigned int a[5]=f("a.dat");
}

But I can't find a simple solution in StackOverflow. Is there anyone can give a way to make it. I guess there are two ways:

  1. Use a program to make the database in hard-code style, code:
a[]={1,2,3,4,5};
  1. use a function to read the data (but that is at runtime), like:
a[5]=f("a.dat");

CodePudding user response:

A third, but also not ideal option would be to #include your data file.

This solution requires you to adapt your data to include a comma between the values though:

a1.dat:

1, 2, 3, 4, 5

cpp1:

#include <iostream>

int main()
{
    constexpr int N = 5;
    unsigned int a[N] = {
        #include "a1.dat"
    };

    for (size_t i=0; i<N;   i)
    {
        std::cout << a[i] << " ";
    }
    std::cout << "\n";
}

But of course, the separation between N (the number of values) and the actual values for the numbers is not good design - N needs to be updated in case the number of values in a.dat changes, which could easily be forgotten.

You might therefore want to choose to move the whole array declaration to the header:

a2.dat:

    unsigned int a[] = {
        1, 2, 3, 4, 5
    };

cpp2:

#include <iostream>

int main()
{
    constexpr int N = 5;
    #include "a2.dat"
    for (size_t i=0; i<N;   i)
    {
        std::cout << a[i] << " ";
    }
    std::cout << "\n";
}

Though this doesn't change the problem with having to specify the number of elements somewhere (and note that leaving out the N in the declaration of a means that now there is no way for the compiler to enforce that there are actually N elements in a)!

To circumvent this, you might want to use an std::vector instead:

a3.dat:

    std::vector<unsigned int> a = {
        1, 2, 3, 4, 5
    };

cpp3:

#include <iostream>
#include <vector>

int main()
{
    #include "a3.dat"
    for (auto anum: a)
    {
        std::cout << anum << " ";
    }
    std::cout << "\n";
}

The std::embed proposal mentioned above also has some comments on this "solution":

Of course, #include expects the format of the data to be source code, and thusly the program fails with spectacular lexer errors

CodePudding user response:

Use xxd that has a "C-style" output, to convert your input for suitable "embedding":

$ xxd -i a.bin
unsigned char a_bin[] = {
  0xd0, 0xe5, 0x46, 0x82, 0x0d, 0xda, 0x72, 0xe8, 0x0f, 0x3f, 0x00, 0x66,
  0xf0, 0xdd, 0x67, 0xd5
};
unsigned int a_bin_len = 16;

The variable names are based on input filename, so name your source files wisely.

This is getting convenient if you have a "build pipeline", which can be as minimal as a Makefile:

a.c: a.bin
    xxd -i $< $@

main: main.o a.o
    $(CC) -o $@ $^

Don't forget to add the generated a.c into your .gitignore or something alike.

  • Related