C++ & C arg_list函数

C++ & the C arg_list functions

本文关键字:list 函数 arg C++      更新时间:2023-10-16

我需要模拟一个库类型,该类型允许将其同时视为char*&std::字符串。例如,这应该是允许的:

char c[16];
MyString ms;
...
strcpy(c, ms);
strcpy(c, ms.c_str());

我只控制库,但不幸的是无法更改此库的使用。我无法更改printf调用,只能更改发送给它的类型!

例如,我可以通过实现来做到这一点

class MyString : public string {
public:
    operator const char*();
};

问题在于:

sprintf(c, "%s", ms);

C"arg_list"函数将不使用char*强制转换。

有什么建议吗?

谢谢。

您只能向...传递一般可复制的POD。

你可以做

struct wrapper { const char * it; };
wrapper it{"hello"};
printf("%s", it);

但问题是,你无法管理内存。一旦您尝试添加构造函数和析构函数来分配/释放内存,就会遇到错误。

错误:无法通过"…"传递非普通可复制类型"class mystr"的对象

你可以帮助自己:

const char * c_str(const char * it) { return it; }
const char * c_str(const std::string& it) { return it.c_str(); }
const char * cstr = "hello";
std::string cppstr = "world";
printf("%s %s!", c_str(cstr), c_str(cppstr));

或者使用boost::format(或类似的ostream)。您应该为C++使用ostream/format(类型安全)。

你说:

问题在于:

sprintf(c,"%s",ms);

C"arg_list"函数将不使用char*铸造。

这里没有石膏。sprintf(..., "%s")不会将ms强制转换为任何值,因此它永远不会调用您的转换运算符。演员阵容为:sprintf(c, "%s", (const char *)ms)。与strcpy不同,va_list不会导致强制,因为没有参数类型。

您可以将对象传递给va_list函数,但需要显式强制转换参数以调用正确的类型转换,或者使用.c_str()

如果printf()可以根据其格式说明符隐式处理C++对象,那么它肯定已经在该语言中了,因为它可能是最需要的库函数之一,而不是从C.映射过来的

如果你打算使用printf()和family,只需写下:

printf("%s", foo.c_str());

或者试试这样的东西(正如@firda在评论中所建议的):

const char * c_str(const std:string &s) { return s.c_str(); }
const char * c_str(const char * s) { return s; }

然后你一直写:

printf("%s%s", c_str(foo), c_str(bar));

普通C++变量函数不能与对象实例一起工作,因为值必须是位可复制的(被调用的函数不知道编译时传递的对象的类型)。

然而,使用C++11,您可以编写一个可变模板,该模板负责使用调用站点类型知识来解包参数:

#include <string>
#include <stdio.h>
#include <string.h>
int mysprintf(char *buf, const char *fmt) {
    return sprintf(buf, "%s", fmt);
}
template<typename T, typename... Args>
int mysprintf(char *buf, const char *fmt, const T& x, Args... args) {
    const char *sp = strchr(fmt, '%');
    if (sp) {
        while (fmt < sp) *buf++ = *fmt++;
        std::string fmt1(fmt, fmt+2);
        int sz = sprintf(buf, fmt1.c_str(), x);
        return sz + mysprintf(buf+sz, fmt+2, args...);
    } else {
        fputs("Invalid format stringn", stderr);
        std::abort();
        return 0;
    }
}
template<typename... Args>
int mysprintf(char *buf, const char *fmt, const std::string& x, Args... args) {
    const char *sp = strchr(fmt, '%');
    if (sp) {
        if (sp[1] != 's') {
            fputs("Invalid format string (%s expected)n", stderr);
            std::abort();
        }
        while (fmt < sp) *buf++ = *fmt++;
        std::string fmt1(fmt, fmt+2);
        int sz = sprintf(buf, fmt1.c_str(), x.c_str());
        return sz + mysprintf(buf+sz, fmt+2, args...);
    } else {
        fputs("Invalid format stringn", stderr);
        std::abort();
        return 0;
    }
}
int main() {
    char buf[200];
    std::string foo = "foo";
    const char *bar = "bar";
    int x = 42;
    mysprintf(buf, "foo='%s', bar='%s', x=%i", foo, bar, x);
    printf("buf = "%s"n", buf);
    return 0;
}

注意:为了简单起见,此代码不处理任何格式化选项或%转义,但允许对裸char *std::string使用%s

注2:我并不是说这是个好主意。。。这是可能的