2015-09-12 47 views
3

我寫了(並使用)我自己的字符串格式化函數,我想簡化功能的使用,具體方式如下所示,但我不確定如何。如何簡化此功能的調用?

下面是相關代碼:

// 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)); 

...與利用可能隱藏在另一個宏的後面。

我該如何做到這一點?

編輯/更新位置參數對我的目的是必不可少的。任何不保留此功能的答案都不是有效答案。

+3

既然您已爲問題C++,我建議你看看[模板參數包(HTTP: //en.cppreference.com/w/cpp/language/parameter_pack)。 –

+0

[va_arg](http://linux.die.net/man/3/va_arg) – kaylum

+0

@AlanAu'format_function _()'已經使用了va_list/va_arg /等等。參數在'any'對象中'包裝'爲了類型安全(和其他原因)。我已經更新了這個問題以包含這些信息。 –

回答

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)); 
+0

但是'any'方法已經被證明不會太安全......無論如何,C++ 11已經過時! – 3442

+0

@KemyLand我不會爲此爭論:),但它確實是OP所要求的(我認爲)。 –

+0

無論如何,這個問題不應該在Code Review SE上嗎? – 3442

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有關的懷舊時刻。
  • 如果您因任何原因更改了任何表達式的類型(即intunsigned),函數會自動適應此情況。
  • (善與惡)編譯器優化可以輕鬆啓動。
  • 該庫的用戶可以通過用用戶定義的類型重載該函數來擴展功能format()的功能。
  • 這增加了動態格式的使用(這是爲了明顯的安全原因)。
  • 這迫使你爲我所謂的位打印創建特殊功能,即以機器可解析的方式進行打印,而不是像人類可讀的那樣,如format()所做的那樣,並且會做。
  • 你可以使用重載功能來自己擴展這個列表:)。
+0

我的格式函數使用格式字符串和格式化選項(十六進制,二進制,十進制精度等)時的位置參數(這對我的目的是必不可少的)以你的例子來說,*可能會增加太多的複雜性,應該是(IMO!)一個簡單的操作。這就是說,我很喜歡這種方法! :) –

+0

@ x-x:這就是我有第二個最後原因的原因。這個函數不是用來提供格式化選項,甚至是位置參數(p.d:POSIX支持'printf()'的位置參數,但是C++標準不支持)。對於'printf()',一個優雅的,模板化的等價物一直希望自C++開始以來,除了'iostream'之外,沒有成功... – 3442

+0

缺點是現在你已經得到了變量和硬編碼的文本都在一起,國際化成爲一種痛苦。'printf'格式的字符串具有明確的優勢。 –