如何简化此功能的调用?

时间:2015-09-12 06:06:40

标签: c++ c macros variadic-functions

我已经编写(并使用)了我自己的字符串格式化功能,我希望以下面所示的特定方式简化该功能的使用,但我不确定如何。< / p>

以下是相关代码:

// Object that can hold a copy of every type I want to print.
// Stores the copy in a union, with an enumeration to identify
// the type. any uses C++ constructors, but could also be implemented
// with C99 designated initializers, like so: https://ideone.com/ElQgBV
struct any
{
    ...
}

// The string format function requires the variable arguments
// to all be of the 'any' type for type safety and (essential for
// my purposes) positional printing.
// Arguments are accessed with a va_list, so essentially
// the variable arguments are treated as an array of any objects. 
char* format_function_(const char* fmt, ...);

// I call the above function with this macro that expands the
// variable arguments and adds a default-constructed sentinel
// at the end. The sentinel is used by the function to count
// how many arguments were passed.
#define format(fmt, ...) format_function_(fmt, __VA_ARGS__, any())

// Calling the function like so, via the above macro...
char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
    any("world"), any("hello"), any(3.14159f), any(42), any((u8)(1<<4)));
// ...returns this string:
// bits:00010000 string:hello world int:0000002A float:3.14

我希望能够像常规*printf样式函数一样调用该函数...

char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
    "world", "hello", 3.14159f, 42, (u8)(1<<4));

...使用隐藏的any对象,可能在另一个宏后面。

我该如何做到这一点?

编辑/更新位置参数对我的目的至关重要。任何不保留此功能的答案都不是有效的答案。

2 个答案:

答案 0 :(得分:2)

也许你喜欢这样的事情? (警告:C ++ 11代码!)

#include <stdio.h>

inline void format() {}

void format(char ch) {
    fputc(ch, stdout);
}

void format(int i) {
    if(i < 0) {
        fputc('-', stdout);
        i = -i;
    }

    int divider = 1;
    while(i / divider >= 10)
        divider *= 10;

    do {
        int digit = i / divider;
        i -= divider * digit;
        divider /= 10;

        fputc('0' + digit, stdout);
    } while(divider > 0);
}

void format(const char *str) {
    fputs(str, stdout);
}

// TODO: Add more 'format()' overloads here!

template<typename FirstArg, typename... OtherArgs>
inline void format(const FirstArg &first, OtherArgs... others) {
    format(first);
    format(others...);
}

然后,你可以简单地......

const char *glorifiedIndex(int index) {
    switch(index % 10) {
        case 1:
            return "st";

        case 2:
            return "nd";

        case 3:
            return "rd";

        default:
            return "th";
    }
}

int main(int argc, const char *const argv[]) {
    format("Hello, world!\n");
    format("My name is ", argv[0], ", and I was given ", argc - 1, " argument", argc != 2 ? "s" : "", ".\n\n");

    for(int i = 1; i < argc; i++)
        format(i, glorifiedIndex(i), " argument: \"", argv[i], "\"\n");

    format("Goodbye, world!\n");
}

这是一个更灵活和优雅的模型,原因如下:

  • 语义安全。
  • 输入安全。
  • 没有<cstdarg>的东西。
  • 没有any的东西。
  • 没有设计得非常糟糕的iostream
  • 实施起来太简单了,我的意思是太多 :)。将这几行代码与典型的3000多行printf.c进行比较。差异在几个数量级!
  • 您可能有与Java和Python相关的怀旧时刻。
  • 如果您因任何原因(即int更改为unsigned)而更改任何表达式的类型,则该函数可以适应此目的。
  • (善与恶)编译器优化可以轻松实现。
  • 库的用户可以通过使用用户定义的类型重载format()函数来扩展其功能。
  • 这使得动态格式的使用成为可能(这是出于明显的安全原因)。
  • 这迫使你为我所谓的位打印创建特殊功能,即以机器可解析的方式打印,而不是像format()那样做,做和将要做的人类可读。
  • 您可以使用重载功能自行扩展此列表:)。

答案 1 :(得分:2)

自从C ++ 11标准以来,有一些名为parameter packs的东西,这使得这很简单:

char* format_function(const char* fmt, ...)
{
    ...
}

template<typename ...T>
char* format(const char* fmt, T... values)
{
    return format_function(fmt, any(values)...);
}

...

char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
                   "world", "hello", 3.14159f, 42, (u8)(1<<4));