C++ 如何简化此函数的调用?

C++ 如何简化此函数的调用?,c++,c,macros,variadic-functions,C++,C,Macros,Variadic Functions,我已经编写(并使用)了我自己的字符串格式化函数,我想以下面所示的特定方式简化该函数的使用,但我不确定如何使用 以下是相关代码: // 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 imp

我已经编写(并使用)了我自己的字符串格式化函数,我想以下面所示的特定方式简化该函数的使用,但我不确定如何使用

以下是相关代码:

// 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
…使用隐藏在另一个宏后面的
任何
对象

我怎样才能做到这一点


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

也许您会喜欢这样的答案?(警告:C++11代码!)

#包括
内联无效格式(){}
void格式(char-ch){
fputc(ch,stdout);
}
无效格式(int i){
if(i<0){
fputc('-',stdout);
i=-i;
}
整数除法器=1;
而(i/除法器>=10)
除法器*=10;
做{
整数位数=i/除法器;
i-=除法器*位;
除法器/=10;
fputc('0'+数字,标准输出);
}而(除法器>0);
}
无效格式(常量字符*str){
fput(str、stdout);
}
//TODO:在此处添加更多“format()”重载!
模板
内联void格式(常量FirstArg&first,其他args…其他){
格式(第一);
格式(其他…);
}
然后,你可以简单地

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");
}
const char*glorifiedIndex(整数索引){
交换机(索引%10){
案例1:
返回“st”;
案例2:
返回“nd”;
案例3:
返回“rd”;
违约:
返回“th”;
}
}
int main(int argc,const char*const argv[]{
格式(“你好,世界!\n”);
格式(“我的名字是”,argv[0],”,而我被赋予了”,argc-1,“参数”,argc!=2?“s”:“,”\n\n”);
对于(int i=1;i
这是一个更灵活、更优雅的模型,原因如下:

  • 语义安全
  • 类型安全
  • 没有
    的东西
  • 没有
    任何
    东西
  • 没有令人难以置信的糟糕设计
    iostream
    之类的东西
  • 它太简单了,无法实现,我的意思是太多了:)。将这几行代码与典型的3000多行代码进行比较
    printf.c
    。差别有几个数量级
  • 您可能会有一些与Java和Python相关的怀旧时刻
  • 如果出于任何原因(即,
    int
    更改为
    unsigned
    )更改任何表达式的类型,则函数将适应此情况
  • (好的和坏的)编译器优化可以很容易地起作用
  • 库的用户可以通过使用用户定义的类型重载函数来扩展
    format()
    函数的功能
  • 这不可能使用动态格式(这是出于明显的安全原因)
  • 这迫使您为我所称的位打印创建特殊功能,即,以机器可解析的方式打印,而不是像
    format()
    did、does和will那样可读
  • 您可以使用重载功能自己扩展此列表:)

自C++11标准以来,有一种称为的东西使得这一点非常简单:

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));
char*格式函数(const char*fmt,…)
{
...
}
模板
字符*格式(常量字符*fmt,T…值)
{
返回格式_函数(fmt,任意(值)…);
}
...
char*str=format(“位:%4b字符串:%1%0整数:%3h浮点:%2.2\n”,

“世界”,“你好”,3.14159F,42,(U8)(1)既然你已经标记了你的问题C++,我建议你查看。@ AlanAu <代码> FrimaTyActudio()/Case>已经使用了VAYList/VAYARG等。这些参数被“包”在<代码>任何< /Cord>对象中,用于类型安全(以及其他原因)。我已更新问题以包含此信息。有一个约定,其中
fmt
字符串的内容控制函数尝试访问的参数数量。这就是
print()
scanf()的内容
函数系列确实如此,并且允许您避免提供哨兵,并减少对宏的需要。如果您使用模板,则不必使用
任何
结构来确保类型安全。实际上,甚至不必使用格式字符串。我的格式函数使用位置参数(这对于我来说非常重要)在格式化字符串和格式化选项(十六进制、二进制、十进制精度等)中,如果添加到您的示例中,可能会给一个简单的操作(IMO!)增加太多的复杂性。也就是说,我非常喜欢这种方法!)这是我有第二个最后的原因。这个函数不是提供格式化选项,甚至是位置参数(P.D:POSIX支持<代码> Prtff()/Cyto>的位置参数,但是C++标准没有)。从C++开始,至今没有成功……除了<代码> IOSWATION/CONT>……缺点是,现在你已经得到了变量,硬编码的文本全部聚合在一起,国际化变成了一种痛苦。<代码> Prtff < /Cord>格式字符串有一定的优势。@ Lexness CraceSein轨道:,标准
printf()
对于国际化来说也不太好。我通常会对这类东西使用预处理器。@KemyLand:怎么会这样?你可以在编译时随意交换格式字符串,而不必在调用站点上乱动变量。这会有多好呢?但是
任何
方法都有可能不是be太安全了……不管怎样,C++11万岁!@KemyLand我对此没有异议:),但它满足了OP的要求(我认为)。不管怎样,这个问题不应该出现在代码评审中吗?
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");
}
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));