2015-06-10 4 views
1

Я в настоящее время есть 2 перегруженных функции:C++ va_list перегрузка Функции

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 правильно в коде.

Есть ли какие-либо другие решения, которые могут быть пропавшими без вести?

EDIT: Я думал о проверке размера va_list внутри функции, и перенаправлять в случае 0, но, насколько я понял, что это невозможно, чтобы получить размер va_list.

+0

вы можете использовать C++ 11? – WaeCo

+0

@WaeCo да, я могу – Vladp

ответ

3
  1. Тип силового сооружения: log(std::string{"hello}). Но это не совсем то, что вам кажется нужным.
  2. В любой из функций вызовите другую.

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

    Но это не очень эффективно, потому что вы будете иметь бесполезную string объект, хотя компилятор может быть в состоянии вставит вызов.

  3. Используйте VARIADIC шаблонов и 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...); 

, но не наоборот.

+0

Извините за плохое форматирование, я не смог заставить другого работать. Если вы можете исправить, спасибо! – edmz

+0

Похоже, что мне нужно, я просто проверю, что он работает для меня и скоро придет. – Vladp

+0

Причина для downvoting? – edmz

0

Вы можете опустить «PRINTF» стиль функции и использовать поток манипулятора (с переменным числом аргументов (VARIADIC шаблона)):

// 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

Смежные вопросы