c++ va_list 函数重载
c++ va_list function overload
我目前有 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
.
的大小
- 强制类型构造:
log(std::string{"hello})
。但这并不是你真正想要的。
在其中一个函数中,调用另一个函数。
void log(const string& s)
{
log(s.c_str());
}
但它不是很有效,因为你会有一个无用的 string
对象,尽管编译器可能能够内联调用。
使用可变模板和 SFINAE:
void log(const string&);
auto log(const char *ptr, Args&& ... args) ->
typename std::enable_if<sizeof...(Args) != 0, void>::type
仅当存在尾随参数时,第二个重载才会在候选函数集中可用。 Showcase。在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
我目前有 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
.
- 强制类型构造:
log(std::string{"hello})
。但这并不是你真正想要的。 在其中一个函数中,调用另一个函数。
void log(const string& s) { log(s.c_str()); }
但它不是很有效,因为你会有一个无用的
string
对象,尽管编译器可能能够内联调用。使用可变模板和 SFINAE:
void log(const string&); auto log(const char *ptr, Args&& ... args) -> typename std::enable_if<sizeof...(Args) != 0, void>::type
仅当存在尾随参数时,第二个重载才会在候选函数集中可用。 Showcase。在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