迭代结构化模板的参数包

Iterating over a parameter pack of structured templates

我希望能够将可变参数函数传递给多个结构化模板(代表传感器读数),并让它遍历这些结构的成员。我的模板和初始值设定项如下所示:

template <typename T>
struct reading_t {
    const char *name;
    const char *fmt;
    const char *units;

    T value;
};

static reading_t<float> longitude = {"Longitude", "%f", "deg", 0.0};
static reading_t<uint32_t> altitude = {"Altitude", "%d", "m", 0};
static reading_t<float> heading = {"Heading", "%f", "deg", 0};

我的最终目标只是一个函数,它吐出一个 reading_t.name 的串联列表,所以来自 C 我从一个典型的可变参数函数开始,它看起来像这样不幸的:

char *concat_readings(char *b, reading_t<auto> *reading...) {
va_list args;
...
}

char buffer[256] = "";
concat_readings(&buffer, &longitude, &altitude, &latitude NULL);

// out << buffer; // Longitude, Altitude, Heading

现在我迷失在 C++ 参数包等的汤里了

PS:我使用的是嵌入式系统,所以不幸的是无法访问标准库。

如果我理解正确......并且如果你想使用类似 printf() 的格式......我想你正在寻找一些东西(假设你标记了 C++14)

template <typename ... Args>
char * concat_readings (char * buffer, Args const & ... as)
 {
   using unused = int[];

   char * b { buffer };

   (void)unused { 0, (std::sprintf(b,
      std::string{"%s "}.append(as.fmt).append(" %s; ").c_str(),
      as.name, as.value, as.units), b+=std::strlen(b), 0)... };

   return buffer;
 }

如果能用C++17,可以稍微简化一下:

template <typename ... Args>
char * concat_readings (char * buffer, Args const & ... as)
 {
   char * b { buffer };

   ((std::sprintf(b,
      std::string{"%s "}.append(as.fmt).append(" %s; ").c_str(),
      as.name, as.value, as.units), b+=std::strlen(b)), ... );

   return buffer;
 }

通话中

char buffer[520];

concat_readings(buffer, longitude, altitude, heading);

std::cout << buffer << '\n';

你得到

Longitude 0.000000 deg; Altitude 0 m; Heading 0.000000 deg;

显然,如果您不能使用 std::stringstd::strlen()std::sprintf(),则必须用等效的东西替换它们。

如果我没理解错,你想在 concat_readings 中使用参数包连接读数的名称,那么你可以进行下一步。

你可以完全不使用任何标准库包含,只需实现你自己的字符串管理功能,如strcat/strcpy。事实上,您可以在嵌入式系统上使用带有模板的完全可用的 C++ 功能,甚至不包括单个标准库头文件,我在我的非常大的嵌入式项目中就是这样做的。 C++ 的模板和其他功能不需要任何标准头文件。也许您还想使用 -fno-exceptions 在 GCC 中禁用异常,如果您根本不使用异常,则 C++ 功能不需要异常。

我使用 strcpy/strcat/printf 只是为了举例,您必须自己使用和实现这些功能的版本。

Try it online!

#include <cstdio>
#include <cstring>
#include <cstdint>

template <typename T>
struct reading_t {
    const char *name;
    const char *fmt;
    const char *units;

    T value;
};

template <typename R>
char * concat_readings(char * buf, reading_t<R> const & reading) {
    strcat(buf, reading.name);
    return buf;
}

template <typename R, typename ... RT>
char * concat_readings(char * buf, reading_t<R> const & reading,
        reading_t<RT> const & ... readings) {
    strcat(buf, reading.name);
    strcat(buf, ", ");
    concat_readings(buf, readings...);
    return buf;
}

int main() {
    static reading_t<float> longitude = {"Longitude", "%f", "deg", 0.0};
    static reading_t<uint32_t> altitude = {"Altitude", "%d", "m", 0};
    static reading_t<float> heading = {"Heading", "%f", "deg", 0};

    char buf[256] = {};
    concat_readings(buf, longitude, altitude, heading);
    printf("%s", buf);
}

输出:

Longitude, Altitude, Heading

我首先错误地实现了你函数的 malloc/free 变体,误读了你的问题。因此,下面提供第一个实现只是为了举例。

假设你可以使用malloc/free,否则你可以自己实现内存分配,我使用malloc/free作为例子。

Try it online!

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cstdint>

template <typename T>
struct reading_t {
    const char *name;
    const char *fmt;
    const char *units;

    T value;
};

char * concat_readings() {
    char * bp = (char*)malloc(1);
    bp[0] = 0;
    return bp;
}

template <typename R, typename ... RT>
char * concat_readings(reading_t<R> const & reading, reading_t<RT> const & ... readings) {
    char * tail = concat_readings(readings...);
    int const bl = strlen(reading.name), tl = strlen(tail);
    char * bp = (char*)malloc(bl + tl + 1);
    strcpy(bp, reading.name);
    strcat(bp, tail);
    free(tail);
    return bp;
}

int main() {
    static reading_t<float> longitude = {"Longitude", "%f", "deg", 0.0};
    static reading_t<uint32_t> altitude = {"Altitude", "%d", "m", 0};
    static reading_t<float> heading = {"Heading", "%f", "deg", 0};

    char * concat = concat_readings(longitude, altitude, heading);
    printf("%s", concat);
    free(concat);
}

输出:

LongitudeAltitudeHeading