从 string/boost::any 映射构建 boost::options

发布于 2024-11-09 14:16:21 字数 926 浏览 5 评论 0原文

我有一张代表配置的地图。它是 std::stringboost::any 的映射。

该地图在开始时初始化,我希望用户能够在命令行上覆盖这些选项。

我想做的是使用 options_description::add_option() 方法从此映射构建程序选项。然而,它需要一个模板参数po::value<>,而我只有boost::any

到目前为止,我只有代码的外壳。 m_Config 代表我的配置类,getTuples() 返回一个 std::mapTuplePairstd::pair 的 typedef,并且 Tuple 包含我感兴趣的 boost::any 有没有

    po::options_description desc;
    std::for_each(m_Config.getTuples().begin(),
                  m_Config.getTuples().end(),
                  [&desc](const TuplePair& _pair)
    {
            // what goes here? :)
            // desc.add_options() ( _pair.first, po::value<???>, "");
    });

办法这样构建它,还是我需要自己做?

提前致谢!

I have a map which represents a configuration. It's a map of std::string and boost::any.

This map is initialized at the start and I'd like the user to be able to override these options on the command line.

What I'd love to do is build the program options from this map using the options_description::add_option() method. However, it takes a template argument po::value<> whereas all I have is boost::any.

So far, I just have the shell of the code. m_Config represents my configuration class, and getTuples() returns a std::map<std::string, Tuple>. TuplePair is a typedef of std::pair<std::string, Tuple> and the Tuple contains the boost::any I am interested in.

    po::options_description desc;
    std::for_each(m_Config.getTuples().begin(),
                  m_Config.getTuples().end(),
                  [&desc](const TuplePair& _pair)
    {
            // what goes here? :)
            // desc.add_options() ( _pair.first, po::value<???>, "");
    });

Is there a way to build it this way, or do I need to resort to doing it myself?

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拧巴小姐 2024-11-16 14:16:21

boost::any 不适用于您的问题。它执行最基本形式的类型擦除:存储和(类型安全)检索,仅此而已。正如您所看到的,无法执行其他操作。正如 jhasse 指出的那样,您可以只测试您想要支持的每种类型,但这是维护的噩梦。

更好的方法是扩展 boost::any 使用的想法。不幸的是,这需要一些样板代码。如果您想尝试一下,现在邮件列表上正在讨论一个新的 Boost 库(标题为“[boost] RFC:类型擦除”),它本质上是一个通用类型擦除实用程序:您可以定义要执行的操作就像您要支持的擦除类型一样,它会生成正确的实用程序类型。 (它可以模拟 boost::any,例如,通过要求擦除的类型是可复制构造和类型安全的,并且可以模拟 boost::function<>

除此之外,您最好的选择可能是自己编写这样的类型 我会为您做的:

#include <boost/program_options.hpp>
#include <typeinfo>
#include <stdexcept>

namespace po = boost::program_options;

class any_option
{
public: 
    any_option() :
    mContent(0) // no content
    {}

    template <typename T>
    any_option(const T& value) :
    mContent(new holder<T>(value))
    {
        // above is where the erasure happens,
        // holder<T> inherits from our non-template
        // base class, which will make virtual calls
        // to the actual implementation; see below
    }

    any_option(const any_option& other) :
    mContent(other.empty() ? 0 : other.mContent->clone())
    {
        // note we need an explicit clone method to copy,
        // since with an erased type it's impossible
    }

    any_option& operator=(any_option other)
    {
        // copy-and-swap idiom is short and sweet
        swap(*this, other);

        return *this;
    }

    ~any_option()
    {
        // delete our content when we're done
        delete mContent;
    }

    bool empty() const
    {
        return !mContent;
    }

    friend void swap(any_option& first, any_option& second)
    {
        std::swap(first.mContent, second.mContent);
    }

    // now we define the interface we'd like to support through erasure:

    // getting the data out if we know the type will be useful,
    // just like boost::any. (defined as friend free-function)
    template <typename T>
    friend T* any_option_cast(any_option*);

    // and the ability to query the type
    const std::type_info& type() const
    {
        return mContent->type(); // call actual function
    }

    // we also want to be able to call options_description::add_option(),
    // so we add a function that will do so (through a virtual call)
    void add_option(po::options_description desc, const char* name)
    {
        mContent->add_option(desc, name); // call actual function
    }

private:
    // done with the interface, now we define the non-template base class,
    // which has virtual functions where we need type-erased functionality
    class placeholder
    {
    public:
        virtual ~placeholder()
        {
            // allow deletion through base with virtual destructor
        }

        // the interface needed to support any_option operations:

        // need to be able to clone the stored value
        virtual placeholder* clone() const = 0;

        // need to be able to test the stored type, for safe casts
        virtual const std::type_info& type() const = 0;

        // and need to be able to perform add_option with type info
        virtual void add_option(po::options_description desc,
                                    const char* name) = 0;
    };

    // and the template derived class, which will support the interface
    template <typename T>
    class holder : public placeholder
    {
    public:
        holder(const T& value) :
        mValue(value)
        {}

        // implement the required interface:
        placeholder* clone() const
        {
            return new holder<T>(mValue);
        }

        const std::type_info& type() const
        {
            return typeid(mValue);
        }

        void add_option(po::options_description desc, const char* name)
        {
            desc.add_options()(name, po::value<T>(), "");
        }

        // finally, we have a direct value accessor
        T& value()
        {
            return mValue;
        }

    private:
        T mValue;

        // noncopyable, use cloning interface
        holder(const holder&);
        holder& operator=(const holder&);
    };

    // finally, we store a pointer to the base class
    placeholder* mContent;
};

class bad_any_option_cast :
    public std::bad_cast
{
public:
    const char* what() const throw()
    {
        return "bad_any_option_cast: failed conversion";
    }
};

template <typename T>
T* any_option_cast(any_option* anyOption)
{
    typedef any_option::holder<T> holder;

    return anyOption.type() == typeid(T) ? 
            &static_cast<holder*>(anyOption.mContent)->value() : 0; 
}

template <typename T>
const T* any_option_cast(const any_option* anyOption)
{
    // none of the operations in non-const any_option_cast
    // are mutating, so this is safe and simple (constness
    // is restored to the return value automatically)
    return any_option_cast<T>(const_cast<any_option*>(anyOption));
}

template <typename T>
T& any_option_cast(any_option& anyOption)
{
    T* result = any_option_cast(&anyOption);
    if (!result)
        throw bad_any_option_cast();

    return *result;
}

template <typename T>
const T& any_option_cast(const any_option& anyOption)
{
    return any_option_cast<T>(const_cast<any_option&>(anyOption));
}

// NOTE: My casting operator has slightly different use than
// that of boost::any. Namely, it automatically returns a reference
// to the stored value, so you don't need to (and cannot) specify it.
// If you liked the old way, feel free to peek into their source.

#include <boost/foreach.hpp>
#include <map>

int main()
{
    // (it's a good exercise to step through this with
    //  a debugger to see how it all comes together)
    typedef std::map<std::string, any_option> map_type;
    typedef map_type::value_type pair_type;

    map_type m;

    m.insert(std::make_pair("int", any_option(5)));
    m.insert(std::make_pair("double", any_option(3.14)));

    po::options_description desc;

    BOOST_FOREACH(pair_type& pair, m)
    {
        pair.second.add_option(desc, pair.first.c_str());
    }

    // etc.
}

如果有不清楚的地方请告诉我。 :)

boost::any is not applicable to your problem. It performs the most basic form of type erasure: storage and (type-safe) retrieval, and that's it. As you've seen, no other operations can be performed. As jhasse points out, you could just test every type you want to support, but this is a maintenance nightmare.

Better would be to expand upon the idea boost::any uses. Unfortunately this requires a bit of boiler-plate code. If you'd like to try it, there's a new Boost library being discussed right now on the mailing list (titled "[boost] RFC: type erasure") that is essentially a generalized type erasure utility: you define the operations you'd like your erased type to support, and it generates the proper utility type. (It can simulate boost::any, for example, by requiring the erased type be copy-constructible and type-safe, and can simulate boost::function<> by additionally requiring the type be callable.)

Aside from that, though, your best option is probably to write such a type yourself. I'll do it for you:

#include <boost/program_options.hpp>
#include <typeinfo>
#include <stdexcept>

namespace po = boost::program_options;

class any_option
{
public: 
    any_option() :
    mContent(0) // no content
    {}

    template <typename T>
    any_option(const T& value) :
    mContent(new holder<T>(value))
    {
        // above is where the erasure happens,
        // holder<T> inherits from our non-template
        // base class, which will make virtual calls
        // to the actual implementation; see below
    }

    any_option(const any_option& other) :
    mContent(other.empty() ? 0 : other.mContent->clone())
    {
        // note we need an explicit clone method to copy,
        // since with an erased type it's impossible
    }

    any_option& operator=(any_option other)
    {
        // copy-and-swap idiom is short and sweet
        swap(*this, other);

        return *this;
    }

    ~any_option()
    {
        // delete our content when we're done
        delete mContent;
    }

    bool empty() const
    {
        return !mContent;
    }

    friend void swap(any_option& first, any_option& second)
    {
        std::swap(first.mContent, second.mContent);
    }

    // now we define the interface we'd like to support through erasure:

    // getting the data out if we know the type will be useful,
    // just like boost::any. (defined as friend free-function)
    template <typename T>
    friend T* any_option_cast(any_option*);

    // and the ability to query the type
    const std::type_info& type() const
    {
        return mContent->type(); // call actual function
    }

    // we also want to be able to call options_description::add_option(),
    // so we add a function that will do so (through a virtual call)
    void add_option(po::options_description desc, const char* name)
    {
        mContent->add_option(desc, name); // call actual function
    }

private:
    // done with the interface, now we define the non-template base class,
    // which has virtual functions where we need type-erased functionality
    class placeholder
    {
    public:
        virtual ~placeholder()
        {
            // allow deletion through base with virtual destructor
        }

        // the interface needed to support any_option operations:

        // need to be able to clone the stored value
        virtual placeholder* clone() const = 0;

        // need to be able to test the stored type, for safe casts
        virtual const std::type_info& type() const = 0;

        // and need to be able to perform add_option with type info
        virtual void add_option(po::options_description desc,
                                    const char* name) = 0;
    };

    // and the template derived class, which will support the interface
    template <typename T>
    class holder : public placeholder
    {
    public:
        holder(const T& value) :
        mValue(value)
        {}

        // implement the required interface:
        placeholder* clone() const
        {
            return new holder<T>(mValue);
        }

        const std::type_info& type() const
        {
            return typeid(mValue);
        }

        void add_option(po::options_description desc, const char* name)
        {
            desc.add_options()(name, po::value<T>(), "");
        }

        // finally, we have a direct value accessor
        T& value()
        {
            return mValue;
        }

    private:
        T mValue;

        // noncopyable, use cloning interface
        holder(const holder&);
        holder& operator=(const holder&);
    };

    // finally, we store a pointer to the base class
    placeholder* mContent;
};

class bad_any_option_cast :
    public std::bad_cast
{
public:
    const char* what() const throw()
    {
        return "bad_any_option_cast: failed conversion";
    }
};

template <typename T>
T* any_option_cast(any_option* anyOption)
{
    typedef any_option::holder<T> holder;

    return anyOption.type() == typeid(T) ? 
            &static_cast<holder*>(anyOption.mContent)->value() : 0; 
}

template <typename T>
const T* any_option_cast(const any_option* anyOption)
{
    // none of the operations in non-const any_option_cast
    // are mutating, so this is safe and simple (constness
    // is restored to the return value automatically)
    return any_option_cast<T>(const_cast<any_option*>(anyOption));
}

template <typename T>
T& any_option_cast(any_option& anyOption)
{
    T* result = any_option_cast(&anyOption);
    if (!result)
        throw bad_any_option_cast();

    return *result;
}

template <typename T>
const T& any_option_cast(const any_option& anyOption)
{
    return any_option_cast<T>(const_cast<any_option&>(anyOption));
}

// NOTE: My casting operator has slightly different use than
// that of boost::any. Namely, it automatically returns a reference
// to the stored value, so you don't need to (and cannot) specify it.
// If you liked the old way, feel free to peek into their source.

#include <boost/foreach.hpp>
#include <map>

int main()
{
    // (it's a good exercise to step through this with
    //  a debugger to see how it all comes together)
    typedef std::map<std::string, any_option> map_type;
    typedef map_type::value_type pair_type;

    map_type m;

    m.insert(std::make_pair("int", any_option(5)));
    m.insert(std::make_pair("double", any_option(3.14)));

    po::options_description desc;

    BOOST_FOREACH(pair_type& pair, m)
    {
        pair.second.add_option(desc, pair.first.c_str());
    }

    // etc.
}

Let me know if something is unclear. :)

世界等同你 2024-11-16 14:16:21
template<class T>
bool any_is(const boost::any& a)
{
    try
    {
        boost::any_cast<const T&>(a);
        return true;
    }
    catch(boost::bad_any_cast&)
    {
        return false;
    }
}

// ...

    po::options_description desc;
    std::for_each(m_Config.getTuples().begin(),
                  m_Config.getTuples().end(),
                  [&desc](const TuplePair& _pair)
    {
        if(any_is<int>(_pair.first))
        {
            desc.add_options() { _pair.first, po::value<int>, ""};
        }
        else if(any_is<std::string>(_pair.first))
        {
            desc.add_options() { _pair.first, po::value<std::string>, ""};
        }
        else
        {
            // ...
        }
    });

// ...

如果您有多种类型,请考虑使用类型列表。

template<class T>
bool any_is(const boost::any& a)
{
    try
    {
        boost::any_cast<const T&>(a);
        return true;
    }
    catch(boost::bad_any_cast&)
    {
        return false;
    }
}

// ...

    po::options_description desc;
    std::for_each(m_Config.getTuples().begin(),
                  m_Config.getTuples().end(),
                  [&desc](const TuplePair& _pair)
    {
        if(any_is<int>(_pair.first))
        {
            desc.add_options() { _pair.first, po::value<int>, ""};
        }
        else if(any_is<std::string>(_pair.first))
        {
            desc.add_options() { _pair.first, po::value<std::string>, ""};
        }
        else
        {
            // ...
        }
    });

// ...

If you have more than a handful of types consider using typelists.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文