如何简化此函数的调用?
How can I simplify the calling of this function?
我已经编写(并使用)了我自己的字符串格式化函数,我想以如下所示的特定方式简化该函数的使用,但我不确定如何做。
相关代码如下:
// 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
对象,可能在另一个宏后面。
我怎样才能做到这一点?
Edit/Update 位置参数对于我的目的来说是必不可少的。任何不保留此功能的答案都不是有效答案。
也许你会喜欢这样的东西? (警告: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()
所做、现在和将来那样的人类可读方式。
- 您可以使用重载功能自己扩展此列表:)。
自从 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));
我已经编写(并使用)了我自己的字符串格式化函数,我想以如下所示的特定方式简化该函数的使用,但我不确定如何做。
相关代码如下:
// 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
对象,可能在另一个宏后面。
我怎样才能做到这一点?
Edit/Update 位置参数对于我的目的来说是必不可少的。任何不保留此功能的答案都不是有效答案。
也许你会喜欢这样的东西? (警告: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()
所做、现在和将来那样的人类可读方式。 - 您可以使用重载功能自己扩展此列表:)。
自从 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));