基于数据类型的printf格式

printf format based on data type

本文关键字:printf 格式 数据类型      更新时间:2023-10-16

我有一个带有简单打印功能的模板缓冲区类。

template <typename valueType, typename sumType, int N>
class IM_buffer
{
public:
IM_buffer()
: bufferValues(), numValues(-1), currentSum() { }
void record(valueType sample)
{
// Set buffer index to be recorded
numValues++;
// Get memory location for oldest element in buffer
valueType& oldest = bufferValues[modIdx(numValues)];
// Add the input value to the current sum and take away the value being replaced
currentSum += sample - oldest;
// And now do the actual replacement in the same memory location
oldest = sample;
}
valueType   getCurrent()            { return bufferValues[modIdx(numValues)];           }
valueType   getNthPrev(int Nprev)   { return bufferValues[modIdx(numValues-Nprev)];     }
sumType     getCurrentSum()         { return currentSum;                                }
double      getAvg()                { return (double) currentSum / MIN(numValues+1, N); }
int         getNumValues()          { return numValues+1;                               }
int         getBufferSize()         { return N;                                         }
void printBuff()
{
for (int ii=0; ii<N; ii++)
{
// if it's an integer type I need:
printf("bufferValues[%2d]=%4dn",ii,bufferValues[ii]);
// but if it's a floating point type I need:
printf("bufferValues[%2d]=%8gn",ii,bufferValues[ii]);
}
}
void clear()
{
for (int ii=0; ii<N; ii++)
bufferValues[ii] = (valueType) 0.0;
numValues = 0;
currentSum = (sumType) 0.0;
}
private:
valueType bufferValues[N];
int numValues;
sumType currentSum;
int modIdx(int a) { return (a % N + N) % N; }
};

然而,printf的格式字符串应该取决于数据类型(例如int、vs.float和vs.double)。我见过这样的讨论,但我真的不想打印出数据类型,我只需要根据数据类型更改printf格式字符串。有人能为我指出如何实现一些条件逻辑以选择正确的printf的正确方向吗?

正如注释中提到的,您应该使用对所有内置类型都有重载的std::cout。然而,如果你真的坚持使用printf,你可以尝试以下破解:

#include <typeinfo>  // for typeid
std::string printfCmd("I wish to print this: ");
// int myVar = 69;  // uncomment to change var type to int
char myVar = 'd';
if (typeid(int) == typeid(myVar)) {
printfCmd += "%d";
} else if (typeid(char) == typeid(myVar)) {
printfCmd += "%c";
} else {
// some warning/error
}
printf(printfCmd.c_str(), myVar);

这不是一个好的解决方案,只有当你真的必须使用它。

如果您不想使用C++重载插入运算符,您可以编写自己的重载函数:

void print(int i) {
printf("%4d",i);
}
void print(double d) {
printf("%8g",d);
}

等等。

现在,您可以在需要时从printBuff函数调用print

printf("bufferValues["%2d] =",ii);
print(bufferValues[ii]);
printf("n");

根据您的情况,另一个选项可能比其他解决方案更好,也可能不更好,那就是在编译时使用模板根据类型对格式说明符进行模板化。

TL;DR示例程序(链接如下):

  • https://godbolt.org/z/3Yz6be67a-总体思路
  • https://godbolt.org/z/fjcrzY87a-省略字段宽度和精度
  • https://godbolt.org/z/h9cGY1Ps1-与cinttypes的平台独立性
  • https://godbolt.org/z/cnMd818Mo-编译时检查错误(可能也是最简洁的例子)

无论如何:

template <typename T> static const char *outFormat = ""; // pick a default.
template <> const char *outFormat<float> = "%8.4f";
template <> const char *outFormat<double> = "%8.4lf";
template <> const char *outFormat<long double> = "%8.4Lf";
template <> const char *outFormat<char> = "%c";

并像这样使用它们,例如:

typedef float some_api_type; // some external libraries unknown type
void printAPIValue (some_api_type value) {
printf(outFormat<some_api_type>, value);
printf("n");
}
// or, for example, printing an stl container...
template <typename Container> void printValues (const Container &c) {
using value_type = typename Container::value_type;
printf("[ ");
for (auto i = c.cbegin(); i != c.cend(); ++ i) {
printf(outFormat<value_type>, *i);
printf(" ");
}
printf("]n");
}

因此,例如这个程序:

some_api_type value = 4;
std::vector<float> floats = { 1.0f, 1.1f, -2.345f };
std::vector<char> chars = { 'h', 'e', 'l', 'l', 'o' };
printAPIValue(value);
printValues(floats);
printValues(chars);

将产生以下输出:

4.0000
[   1.0000   1.1000  -2.3450 ]
[ h e l l o ]

上面有很多不同的形式,所以你必须做一些适合你的事情。例如:

为了方便起见,我指定了%和字段宽度/精度。您可以删除所有这些并根据需要构建它,在这种情况下,使用std::string可能会节省您以后的一些键入。此程序:
template <typename T> static string outFormat;
template <> const string outFormat<float> = "f";
template <> const string outFormat<int> = "d";
// graceful handling of unsupported types omitted.
template <typename Container> void printValues (const Container &c) {
using value_type = typename Container::value_type;
int index = 0;
printf("list with format %s:n", outFormat<value_type>.c_str());
for (auto i = c.cbegin(); i != c.cend(); ++ i, ++ index) {
string format = "item[%d] = %10" + outFormat<value_type> + "n";
printf(format.c_str(), index, *i);
}
}
int main () {
std::vector<float> floats = { 1.0f, 1.1f, -2.345f };
std::vector<int> ints = { -1, 1, -2, 9 };
printValues(floats);
printValues(ints);
}

输出:

list with format f:
item[0] =   1.000000
item[1] =   1.100000
item[2] =  -2.345000
list with format d:
item[0] =         -1
item[1] =          1
item[2] =         -2
item[3] =          9
  • 您还可以使用cinttypes(C的inttypes.h)来进行一些与平台无关的东西,例如这个程序:

    #include <cstdio>
    #include <cinttypes> // types and PRI* sized format specifiers
    template <typename T> static const char *hexFormat = "<unsupported type>";
    template <> const char *hexFormat<uint8_t> = "0x%02" PRIx8;
    template <> const char *hexFormat<uint16_t> = "0x%04" PRIx16;
    template <> const char *hexFormat<uint32_t> = "0x%08" PRIx32;
    template <> const char *hexFormat<uint64_t> = "0x%016" PRIx64;
    template <typename UIntValue> void printHex (UIntValue value) {
    printf(hexFormat<UIntValue>, value);
    printf("n");
    }
    int main () {
    printHex((size_t)0xABCD1234);
    printHex(9U);
    printHex(9UL);
    printHex((unsigned char)'!');
    printHex(9.0);
    }
    

    在64位平台上,可能会输出:

    0x00000000abcd1234
    0x00000009
    0x0000000000000009
    0x21
    <unsupported type>
    
  • 您有各种选项来强制使用支持的类型,我不打算列出它们,请发挥您的想象力。但是由于您使用的是模板,因此如果需要,可以进行一些编译时检查。例如,这个程序,使用static_assert:

    #include <cstdio>
    template <typename T> static constexpr const char *format = nullptr;
    template <> constexpr const char * format<short> = "%hdn";
    template <> constexpr const char * format<int> = "%dn";
    template <> constexpr const char * format<long> = "%ldn";
    template <typename Value> void print (Value value) {
    static_assert(format<Value> != nullptr, "Unsupported type.");
    printf(format<Value>, value);
    }
    int main () {
    print<short>(1);
    print<int>(1);
    print<float>(1);  // <-- compilation will fail here
    print<long>(1);
    }
    

    将无法使用编译print<float>

    error: static assertion failed: Unsupported type.
    

    这可以帮助您发现错误。


  • 关于这一切,可能还有很多话要说,但我厌倦了打字,需要回去工作。希望这对某些人有帮助。