从自定义printf函数返回一个const char *

时间:2013-11-13 09:30:15

标签: c++ c printf variadic-functions

我已经编写了自定义打印功能。我的问题是我需要返回一个const char*,因为这必须在另一个函数中使用。我根本不知道如何管理......

anotherFunction (const char* text /*Here*/, unsigned __int32 value, unsigned __int64 bigVal);

我知道以下示例/ s不能正常工作。那是我迄今为止所尝试过的。

const char* CatchMessage (const char *message, ...)
{
    va_list  args;
    va_start (args, message);
    /*?*/
    va_end   (args);
    return message;
}

我只是设法在cmd中获得正确的输出,但实际上我需要它作为返回值。

void CatchMessage (const char *message, ...)
{
    va_list  args;
    va_start (args, message);
    vfprintf (stdout, message, args);
    va_end   (args);
}

呼叫:

CatchMessage ("Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);

应该返回:

"Some Input stuff and 12 equals to 6"

我一直无法找到解决方案。任何帮助将不胜感激。

问:如何让此CatchMessage函数返回格式正确的const char*

6 个答案:

答案 0 :(得分:6)

听起来CatchMessage应该指向char缓冲区(及其大小),vsnprintf()指向该缓冲区。

答案 1 :(得分:3)

由于您正在使用C ++(至少根据问题上的标签),为什么不在std::string中返回字符串?

答案 2 :(得分:2)

返回(constchar *的问题是你必须在某个地方有一个缓冲区。

有几种方法可以实现这一目标:

  1. 调用者必须提供该缓冲区
  2. 您必须malloc()
  3. 函数本身作为static缓冲区,但这会使其不可重入 - 这对于多线程等是不利的。
  4. 广告1:

    void CatchMessage(char * result, size_t maxlen, const char *message, ...)
    {
        va_list ap;
        va_start(ap, message);
        vsnprintf(result, maxlen, message, ap);
        va_end(ap);
    }
    

    调用
    char buffer[500];
    CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
    anotherfunction(buffer, ...)
    

    ad 2:

    char * CatchMessage(const char *message, ...)
    {
        size_t size = 500;
        char * result = malloc(size);
        if (!result) return NULL; // error handling!
        while (1) {
            va_list ap;
            va_start(ap, message);
            size_t used = vsnprintf(result, size, message, ap);
            va_end(ap);
            char * newptr = realloc(result, size);
            if (!newptr) { // error
                free(result);
                return NULL;
            }
            result = newptr;
            if (used <= size) break;
            size = used;
        }
        return result;
    }
    

    调用
    char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
    if (!buffer) { /* error handling: no memory! */ }
    anotherfunction(buffer, ...)
    free(buffer); // important for avoiding memory leaks
    

    广告3:

    char * CatchMessage(const char *message, ...)
    {
    
        static char result[500]; // static is important here! Otherwise the memory will be freed immediately after returning.
        va_list ap;
        va_start(ap, message);
        vsnprintf(result, sizeof result, message, ap);
        va_end(ap);
        return result;
    }
    

    调用
    char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
    anotherfunction(buffer, ...)
    

    没有其他选择,尤其是没有定义

    char result[500];
    
    函数中的

    然后返回它:这个数组存在于堆栈中并在返回后立即释放。呼叫者无法安全访问它;它的内容是未定义的。

答案 3 :(得分:1)

如果你不关心重新入侵,你可以返回指向静态缓冲区的指针:

#define MESSAGE_MAX 1024

const char *
CatchMessage (const char *message, ...)
{
    static buffer[MESSAGE_MAX];
    va_list  args;
    va_start (args, message);
    vsnprintf (buffer, MESSAGE_MAX, message, args);
    va_end   (args);
    return buffer;
}

注意:

  1. 此实现不是线程安全的。如果您关心线程安全,请使用线程本地存储而不是静态缓冲

  2. 此实现具有硬编码的邮件长度上限。如果这不合适,并且您的编译器符合C99,则可以使用vsprintf作为第一个参数调用NULL来了解结果字符串长度,然后分配该侧的缓冲区。

答案 4 :(得分:0)

在C ++中,可变参数的使用是,因为它特别强调类型安全性(和内存安全性)。因此,您可能想要提出一个实际上类型安全的版本(是的,这是可能的),并提出了类似的接口:

template <typename H, typename... Args>
void format(std::ostream& out,
            char const* format,
            size_t len,
            H const& head,
            Args const&... args);

// Variations with 'char const (&)[N]' and 'std::string' formats
// as well as variations returning directly a 'std::string'.

现在,format的实施并不太难;特别是不支持位置参数。简单的一个可以在下面找到:

inline void format_string(std::ostream& out, char const* const* c) { out << *c; }
inline void format_string(std::ostream& out, std::string const* s) { out << *s; }
inline void format_string(std::ostream& out, void const*); // will throw

template <typename Integral, typename = enable_integral<Integral>::type>
inline void format_integral(std::ostream& out, Integral const* i) { out << *i; }
inline void format_integral(std::ostream& out, void const*); // will throw

inline size_t format_consume(std::ostream& out,
                             char const* const format,
                             size_t const length)
{
    char const* end = format + length;
    char const* current = format;

    do {
        // 1. Find first "format identifier", output stuff in-between
        char const* perc = std::find(current, end, '%');

        if (perc != current) { out.write(current, perc - current); }

        current = perc;

        // 2. %% is % escaped by %, so output it directly
        while (*current == '%' and *(current + 1) == '%') {
            out.put('%');
            current += 2;
        }
    } while (current != end and *current != '%');

    // 3. Return number of characters of format parameter consumed
    return current - format;
} // format_consume

inline void format(std::ostream& out, char const* format, size_t len) {
     size_t const consumed = format_consume(out, format, len);

     if (consumed != len) { throw std::runtime_exception("Missing arguments"); }
} // format

template <typename H, typename typename... Args>
void format(std::ostream& out,
            char const* format,
            size_t len,
            H const& head,
            Args const&... args)
{
     size_t const consumed = format_consume(out, format, len);

     if (consumed == len) { throw std::runtime_exception("Extraneous arguments"); }

     format += consumed;
     len -= consumed;

     assert(*format == '%');

     switch(*(format+1)) {
     case 's': format_string(out, &head); break;
     case 'd': format_integral(out, &head); break;
     default: throw std::runtime_exception("Invalid specifier");
     }

     format(out, format+2, len-2, args...);
} // format

在一般情况下,它稍微多毛,因为你需要解析修饰符等...但是,对于生产就绪的实现,我建议看一下Boost.Format。

答案 5 :(得分:-3)

char* CatchMessage (size_t size, const char *message, ...)
{
    char result[size];
    va_list  args;
    va_start (args, message);
    vsprintf (result, message, args);
    va_end   (args);
    return result;
}

请参阅this以供参考。

对于glglgl:如果你想使用干净的方式,请使用vsnprintf(result, size, message, args)