2016-06-27 4 views
0

Я пробую свои первые шаги к шаблонам. У меня есть класс, который считывает параметры конфигурации с помощью boost/property_tree. В настоящее время у меня есть десятки геттеров, которые все равно делают. Например:C++ компилятор не применяет шаблон

inline std::string getSocket(void) { 
    return any_cast<std::string>(param["socket"]); 
} 

Теперь я попытался создать шаблон:

Декларация:

template <typename T, typename R> 
R getValue(const std::string &); 

Определение:

template <typename T=std::string, typename R=std::string> 
R MilterCfg::getValue(const std::string &key) { 
    if (param.count(key) == 0) 
     return ""; 
    return any_cast<T>(param[key]); 
} 

вызова в другой CPP-файл:

mfsocket = ::config->getValue<>("socket"); 

компилятор не принимает это:

/Users/croessner/ClionProjects/sigh/src/milter.cpp:491:30: error: no matching member function for call to 'getValue' 
    mfsocket = ::config->getValue<>("socket"); 
       ~~~~~~~~~~^~~~~~~~~~ 
/Users/croessner/ClionProjects/sigh/src/config.h:112:11: note: candidate template ignored: couldn't infer template argument 'T' 
    R getValue(const std::string &); 
    ^

я попробовать это на Mac OS X El-Capitan с компилятором Clang ++. Я думаю, что мне очень не хватает понимания шаблонов. Но что мне здесь не хватает?

Вот полный заголовок:

#ifndef SRC_CONFIG_H_ 
#define SRC_CONFIG_H_ 

#include <map> 
#include <string> 

#include <boost/any.hpp> 
#include <boost/program_options/variables_map.hpp> 

namespace po = boost::program_options; 

extern bool debug; 

namespace conf { 
    using boost::any_cast; 

    typedef std::map<std::string, boost::any> config_t; 

    /*! 
    * \brief Read a configuration file and store settings 
    * 
    * All milter settings may be stored in a configuration file. This class 
    * reads a default configuration file, if not given as command line 
    * argument and extracts all keys and values. For each key that is not 
    * found, but requested my the milter, a default value is defined in a 
    * local data structure. 
    */ 
    class MilterCfg { 
    public: 
     MilterCfg(const po::variables_map &); 

     virtual ~MilterCfg() = default; 

     /*! 
     * \brief The milter socket 
     * 
     * The socket may have one of three formats. First is 
     * inet:portnumber\@host, second is inet6:portnumber\@host6 or a unix 
     * socket like unix:/pat/to/socket. host and host6 may be a hostname 
     * or a valid IP address. IPv6 addresses must be written in squared 
     * braces. 
     */ 
     inline std::string getSocket(void) { 
      return any_cast<std::string>(param["socket"]); 
     } 

     /*! 
     * The milter will drop its privileges to this user 
     */ 
     inline std::string getUser(void) { 
      return any_cast<std::string>(param["user"]); 
     } 

     /*! 
     * The milter will drop its privileges to this group 
     */ 
     inline std::string getGroup(void) { 
      return any_cast<std::string>(param["group"]); 
     } 

     /*! 
     * \brief An optional PID file 
     * 
     * If desired, a PID file may be created on startup. It will be 
     * automatically removed, when the milter gets stopped again. 
     */ 
     inline std::string getPidFile(void) { 
      return any_cast<std::string>(param["pidfile"]); 
     } 

     /*! 
     * \brief Map file containing S/MIME certificates 
     * 
     * This file contains a mapping between email addresses and 
     * associated S/MIME certificates and keys. 
     */ 
     inline std::string getMapFile(void) { 
      return any_cast<std::string>(param["mapfile"]); 
     } 

     /*! 
     * \brief Path to a temporary directory 
     * 
     */ 
     inline std::string getTmpDir(void) { 
      return any_cast<std::string>(param["tmpdir"]); 
     } 

#if !__APPLE__ && !defined _NOT_DAEMONIZE 
     /*! 
     * \brief Bring the milter to background 
     * 
     * The milter gets a daemon. The root path is set to '/' and the 
     * standard in and out channels are closed 
     */ 
     inline bool getDaemon(void) { 
      return any_cast<bool>(param["daemon"]); 
     } 
#endif // !__APPLE__ && !defined _NOT_DAEMONIZE 

     template <typename T, typename R> 
     R getValue(const std::string &); 

    private: 
     /*! 
     * \brief Data store for configuration settings 
     */ 
     config_t param; 

     /*! 
     * \brief Default settings for the milter 
     * 
     * If a required setting could not be read from the configuration, a 
     * default setting will be used from this data structure. 
     */ 
     struct { 
      //! \brief Milter socket 
      std::string socket = "inet:[email protected]"; 
      //! \brief Milter system user 
      std::string user = "milter"; 
      //! \brief Milter system group 
      std::string group = "milter"; 
      //! \brief Optional PID file 
      std::string pidfile = std::string(); 
#if !__APPLE__ && !defined _NOT_DAEMONIZE 
      //! \brief Run the milter as a daemon process 
      bool daemon   = false; 
#endif // !__APPLE__ && !defined _NOT_DAEMONIZE 
      //! \brief Location for the map file 
      std::string mapfile = std::string(); 
      //! \brief Location for temporary files 
      std::string tmpdir = "/tmp"; 
     } defaults; 
    }; 
} // namespace conf 

#endif // SRC_CONFIG_H_ 

И файл CPP:

#include "config.h" 

#include <iostream> 

#include <boost/filesystem.hpp> 
#include <boost/property_tree/ptree.hpp> 
#include <boost/property_tree/ini_parser.hpp> 

namespace fs = boost::filesystem; 

namespace conf { 
    using boost::any_cast; 

    MilterCfg::MilterCfg(const po::variables_map &vm) { 
     std::string conffile = vm["config"].as<std::string>(); 

     boost::property_tree::ptree pt; 
     try { 
      if (fs::exists(fs::path(conffile)) 
       && fs::is_regular(fs::path(conffile))) { 
       boost::property_tree::ini_parser::read_ini(conffile, pt); 
      } else { 
       std::cerr << "Error: Unable to read config file " 
          << conffile << std::endl; 
      } 
     } 
     catch (const std::exception &e) { 
      std::cerr << "Error: " << e.what() << std::endl; 
     } 

     try { 
      param["socket"] = pt.get<std::string>("Milter.socket"); 
     } 
     catch (...) { 
      param["socket"] = defaults.socket; 
     } 

     try { 
      param["user"] = pt.get<std::string>("Milter.user"); 
     } 
     catch (...) { 
      param["user"] = defaults.user; 
     } 

     try { 
      param["group"] = pt.get<std::string>("Milter.group"); 
     } 
     catch (...) { 
      param["group"] = defaults.group; 
     } 

     try { 
      param["pidfile"] = pt.get<std::string>("Milter.pidfile"); 
     } 
     catch (...) { 
      param["pidfile"] = defaults.pidfile; 
     } 

     try { 
      param["mapfile"] = pt.get<std::string>("Milter.mapfile"); 
     } 
     catch (...) { 
      param["mapfile"] = defaults.mapfile; 
     } 

     try { 
      param["tmpdir"] = pt.get<std::string>("Milter.tmpdir"); 
     } 
     catch (...) { 
      param["tmpdir"] = defaults.tmpdir; 
     } 

#if !__APPLE__ && !defined _NOT_DAEMONIZE 
     try { 
      param["daemon"] = pt.get<bool>("Milter.daemon"); 
     } 
     catch (...) { 
      param["daemon"] = defaults.daemon; 
     } 
#endif // !__APPLE__ && !defined _NOT_DAEMONIZE 

     if (::debug) { 
      std::cout << "Configuration file values:" << std::endl; 

      std::cout << "user=" 
       << any_cast<std::string>(param["user"]) 
       << std::endl; 
      std::cout << "group=" 
       << any_cast<std::string>(param["group"]) 
       << std::endl; 
      std::cout << "socket=" 
       << any_cast<std::string>(param["socket"]) 
       << std::endl; 
      std::cout << "pidfile=" 
       << any_cast<std::string>(param["pidfile"]) 
       << std::endl; 
#if !__APPLE__ && !defined _NOT_DAEMONIZE 
      std::cout << "daemon=" 
       << std::boolalpha << any_cast<bool>(param["daemon"]) 
       << std::endl; 
#endif // !__APPLE__ && !defined _NOT_DAEMONIZE 
      std::cout << "mapfile=" 
       << any_cast<std::string>(param["mapfile"]) 
       << std::endl; 
      std::cout << "tmpdir=" 
       << any_cast<std::string>(param["tmpdir"]) 
       << std::endl; 
     } 
    } 

    template <typename T=std::string, typename R=std::string> 
    R MilterCfg::getValue(const std::string &key) { 
     if (param.count(key) == 0) 
      return ""; 
     return any_cast<T>(param[key]); 
    } 

} // namespace conf 

Итак, вы видите, много повторять логику. Я чувствую, что это можно сделать более общим. Не могли бы вы дать мне подсказку? Заранее спасибо

ответ

4

По умолчанию аргументы идут на декларации, а не определение:

// declaration 
template <typename T=std::string, typename R=std::string> 
R getValue(const std::string &); 

// definition 
template <typename T, typename R> 
R MilterCfg::getValue(const std::string &key) { 
    if (param.count(key) == 0) 
     return ""; 
    return any_cast<T>(param[key]); 
} 

нотабене угловые скобки на сайте вызова разрешены, но являются излишними; следующий более идиоматический:

mfsocket = ::config->getValue("socket"); 
+0

Хорошо, я изменил, но это дает новую erros: 'Неопределенным символы для архитектуры x86_64: «станд :: __ 1 :: basic_string <голец, станд :: __ 1 :: char_traits , std :: __ 1 :: allocator > conf :: MilterCfg :: getValue , std :: __ 1 :: allocator >, std :: __ 1 :: basic_string , std :: __ 1 :: allocator >> (std :: __ 1 :: basic_string , std: : __ 1 :: распределитель > const &) ", ссылка от: _main in milter.cp p.o ld: символ (-ы) не найден для архитектуры x86_64' –

+1

@ ChristianRößner Nice! Ваша проблема решена :) Я предлагаю вам отметить это как принятый ответ и [задать вопрос _new_] (http://stackoverflow.com/questions/ask) для вашей новой проблемы :) –

+2

@ ChristianRößner: [Определения шаблонов go в файлах заголовков, а не в исходных файлах] (https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl). – ildjarn

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