C++ va_list函数重载

c++ va_list function overload

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

我目前有 2 个函数重载:

void log(const char* format, ...);
void log(const string& message);

我希望在此调用的情况下:log("hello");调用字符串版本,或者换句话说,只有在 2 个或更多参数的情况下才应调用第一个重载。

我想过这样做:

template<typename T>
void log(const char* format, T first, ...);

但在这种情况下,我将无法在代码中正确使用va_list

我可能缺少其他解决方案吗?

编辑:我想过从函数内部检查va_list的大小,并在 0 的情况下重定向,但据我了解,不可能获得 va_list 的大小。

  1. 力型结构:log(std::string{"hello})。但这似乎并不是你想要的。
  2. 在任一函数中,调用另一个函数。

    void log(const string& s)  
    {
          log(s.c_str());      
    }
    

    但它不是很有效,因为您将拥有一个无用的string对象,尽管编译器可能能够内联调用。

  3. 使用可变参数模板和 SFINAE:

    void log(const string&);
    auto log(const char *ptr, Args&& ... args) -> 
         typename std::enable_if<sizeof...(Args) != 0, void>::type 
    

    仅当存在尾随参数时,第二个重载才会在候选函数集中可用。展示。在 C++14 中,您可以使用 std::enable_if 的速记版本,std::enable_if_t ,这使得语法更加清晰:

    auto log(const char *ptr, Args&& ... args) -> std::enable_if_t<sizeof...(Args) != 0, void>
    

    您仍然可以在 C++11 中使用

    template <bool B, typename T>
    using enable_if_t = typename std::enable_if<B, T>::type;
    

如果你调用一个接受va_list的函数(比如printf),你仍然可以扩展参数包:

std::printf(ptr, args...);

但反之亦然。

您可以省略"printf"函数样式并使用流操纵器(采用可变数量的参数(可变参数模板)):

// Header
// ============================================================================
#include <iostream>
#include <sstream>
#include <tuple>
// Format
// ============================================================================
namespace Detail {
    template<unsigned I, unsigned N>
    struct Format;
    template<unsigned N>
    struct Format<N, N> {
        template<typename... Args>
        static void write(std::ostream&, const std::tuple<Args...>&, std::size_t offset)
        {}
    };
    template<unsigned I, unsigned N>
    struct Format {
        template<typename... Args>
        static void write(
            std::ostream& stream,
            const std::tuple<Args...>& values,
            std::size_t offset)
        {
            if(offset == 0) stream << std::get<I>(values);
            else Format<I+1, N>::write(stream, values, offset - 1);
        }
    };
    class FormatParser
    {
        public:
        const char* fmt;
        const std::size_t size;
        FormatParser(const char* fmt, std::size_t size)
        : fmt(fmt), size(size)
        {}
        virtual ~FormatParser() {}
        void write(std::ostream& stream) const;
        protected:
        virtual void write_value(std::ostream&, std::size_t index) const = 0;
    };
} // namespace Detail
template<typename... Args>
class Format : public Detail::FormatParser
{
    public:
    typedef std::tuple<const Args&...> Tuple;
    static constexpr std::size_t Size = std::tuple_size<Tuple>::value;
    const std::tuple<const Args&...> values;
    Format(const char* fmt, const Args&... values)
    : Detail::FormatParser(fmt, Size), values(values...)
    {}
    protected:
    void write_value(std::ostream& stream, std::size_t index) const {
        Detail::Format<0, Size>::write(stream, values, index);
    }
};
template <typename... Args>
inline Format<Args...> format(const char* fmt, const Args&... values) {
    return Format<Args...>(fmt, values...);
}
template <typename... Args>
inline std::ostream& operator << (std::ostream& stream, const Format<Args...>& format) {
    format.write(stream);
    return stream;
}
template <typename... Args>
inline std::string format_string(const char* fmt, const Args&... values) {
    std::ostringstream result;
    result << format(fmt, values...);
    return result.str();
}
// Source
// ============================================================================
#include <cctype>
#include <cstdlib>
#include <stdexcept>
namespace Detail {
    void FormatParser::write(std::ostream& stream) const {
        const char* s = fmt;
        while(*s) {
            switch(*s) {
                case '{':
                if(*(++s) != '{') {
                    char* end;
                    unsigned long index = std::strtoul(s, &end, 10);
                    while(*end != '}') {
                        if( ! std::isspace(*end)) {
                            s = end;
                            if( ! *s) s = "End";
                            throw std::runtime_error(std::string(
                                "Invalid Format String `") + fmt + "` at " + s);
                        }
                        ++end;
                    }
                    s = end + 1;
                    if(index < size) write_value(stream, index);
                    else throw std::runtime_error(std::string(
                        "Invalid Format Index `")  + std::to_string(index)
                        + "` in `" + fmt + '`');
                    continue;
                }
                break;
                case '}':
                if(*(++s) !=  '}') {
                    if( ! *s) s = "End";
                    throw std::runtime_error(
                        std::string("Invalid Format String `") + fmt + "`"
                        "Missing `}` at " + s);
                }
                break;
            }
            stream.put(*s++);
        }
    }
} // namespace Detail

// Usage
// ============================================================================
int main() {
    // a = 1; b = 2; 1 + 2 = 3
    std::cout << format("a = {0}; b = {1}; {0} + {1} = {2}", 1, 2, 3) << "n";
}

另外:看看 boost::format