Search code examples
c++ffmpegmacrosc-preprocessortoken-pasting-operator

How to call macro that uses token pasting?


I am trying to print ffmpeg version in a C++ program. I see that in the /libavutil/version.h there is AV_VERSION which should tell the version number in the format x.x.x.

As a test I used some random numbers as function parameters like this: std::string version = AV_VERSION(3,4,2);. The same error I get if I use LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR and LIBAVUTIL_VERSION_MICRO from the file. That was actually my first try to print the version number.

The error I get is invalid suffix '.2' on floating constant or invalid suffix '.101' on floating constant if I try to print std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;

I do understand that the preprocessor is thinking that the token is a float, hence the error. How do you actually use this type of macro funtion?

That macro is in the file I mentioned above, so it must be a way to call that macro function without giving an error, thinking that is a mature library, and I guess other libraries use something similar for printing version number.

Here is how AV_VERSION is defined in the header file and how I call it:

 #define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
 #define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c
 #define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c)

 #define AV_VERSION_MAJOR(a) ((a) >> 16)
 #define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8)
 #define AV_VERSION_MICRO(a) ((a) & 0xFF)

 #define LIBAVUTIL_VERSION_MAJOR  57
 #define LIBAVUTIL_VERSION_MINOR   9
 #define LIBAVUTIL_VERSION_MICRO 101

 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                            LIBAVUTIL_VERSION_MINOR, \
                                            LIBAVUTIL_VERSION_MICRO)
 #define LIBAVUTIL_VERSION       AV_VERSION(LIBAVUTIL_VERSION_MAJOR,     \
                                        LIBAVUTIL_VERSION_MINOR,     \
                                        LIBAVUTIL_VERSION_MICRO)

 int main()
 {
     std::string version = AV_VERSION(3,4,2);
     std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;

     return 0;
 }

I coud've skip this error but as I'm trying to learn C++ I am pretty sure that I will find more of this type of macros so no point to avoid learning them now as I'm facing them.

Thanks in advance!


Solution

  • You need to use a stringize expansion. Because of how the preprocessor works, this involves two macros:

    #define STR(x) #x
    #define XSTR(x) STR(x)
    

    The macro STR will take whatever parameter you give it and make that a string literal.

    The macro XSTR will first expand its parameter x and the result will be the parameter to STR.

    To illustrate:

    • STR(LIBAVUTIL_VERSION) will give "LIBAVUTIL_VERSION"
    • XSTR(LIBAVUTIL_VERSION) will give "57.9.101"

    Demo according to your code:

    int main()
    {
        std::string version1 = XSTR(LIBAVUTIL_VERSION);
        std::string version2 = XSTR(AV_VERSION(3,4,2));
        std::cout << version1 << "\n";
        std::cout << version2 << "\n";
        return 0;
    }
    

    Output:

    57.9.101
    3.4.2