зачем определять оператор << и >> в разрыве пространства имен boost program_options? - PullRequest
1 голос
/ 06 мая 2020

из здесь , если я помещу перегруженный оператор в пространство имен, он должен просто работать. Но я обнаружил, что на самом деле он не работает, компилятор всегда жалуется, что не может найти << или >>.

В следующем примере , если я включаю USE_NAMESPACE, компиляция не выполняется. Но >> и << на самом деле работают вне program_options. Если я отключу ENABLE_PO, он будет работать нормально. </p>

Что здесь происходит? как это исправить?

#include <iostream>
#include <sstream>
#include <string>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#define ENABLE_PO 1
#define USE_NAMESPACE 0
enum class test_enum_t {bar, foo};
#if USE_NAMESPACE
namespace test_enum
{
#endif

    template<typename istream_T>
    istream_T& operator>>(istream_T& in, test_enum_t& value)
    {
        std::string in_str;
        in >> in_str;
        if (in_str == "bar")
            value = test_enum_t::bar;
        else if(in_str == "foo")
            value = test_enum_t::foo;
        else
            in.setstate(std::ios_base::failbit);
        return in;
    }

    template<typename ostream_T>
    ostream_T &operator<<(ostream_T &out, const test_enum_t& value) {
        if (value == test_enum_t::bar)
            out << "bar";
        else if (value == test_enum_t::foo)
            out << "foo";
        else
            out << "unknown-value";
        return out;
    }

#if USE_NAMESPACE
}
using namespace test_enum;
#endif

#if ENABLE_PO
int simple_options(int argc, char *argv[])
{
    test_enum_t test = test_enum_t::bar;
    po::options_description config("Configuration");
    config.add_options()
        ("help", "produce help message")
        ("in,i", po::value(&test)->default_value(test), "test enum class as input.")
        ;
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, config),vm);
    po::notify(vm);
    if (vm.count("help")) {
        std::cout << config << "\n";
        return 1;
    }

    return 0;
}
#endif
int main(int argc, char *argv[])
{
#if ENABLE_PO
    simple_options(argc, argv);
#endif
    test_enum_t test;
    std::cout << "input bar or foo" << std::endl;
    std::cin >> test;
    if (std::cin.fail())
    {
        std::cerr << "not a valid value";
        exit(1);
    }
    std::cout << test << std::endl;
    return 0;
}

1 Ответ

1 голос
/ 08 мая 2020

Как уже упоминалось, для успешного выполнения вам потребуется ADL (поиск, зависящий от аргументов).

Если вы можете управлять POI (Point Of Instantiation), вы также можете use активно использовать имена из выделенного пространства имен. В этом случае вам необходимо use эти имена в TU, который определяет simple_options, перед точкой, которая создает экземпляр кода синтаксического анализа, например, где вы определяете компонент описания опций .

Вот упрощенная версия кода, которая это демонстрирует

Live On Coliru

#include <boost/program_options.hpp>
#include <iostream>
#include <sstream>
#include <string>
enum class test_enum_t { bar, foo };

namespace test_enum {

    template <typename istream_T>
        istream_T& operator>>(istream_T& in, test_enum_t& value) {
            std::string in_str;
            in >> in_str;
            if (in_str == "bar")
                value = test_enum_t::bar;
            else if (in_str == "foo")
                value = test_enum_t::foo;
            else
                in.setstate(std::ios_base::failbit);
            return in;
        }

    template <typename ostream_T>
        ostream_T& operator<<(ostream_T& out, const test_enum_t& value) {
            if (value == test_enum_t::bar)
                out << "bar";
            else if (value == test_enum_t::foo)
                out << "foo";
            else
                out << "unknown-value";
            return out;
        }

}

using test_enum::operator<<;
using test_enum::operator>>;

test_enum_t simple_options(int argc, char* argv[]) {
    namespace po = boost::program_options;

    test_enum_t test = test_enum_t::bar;
    po::options_description config("Configuration");

    config.add_options()("help", "produce help message")(
            "in,i", po::value(&test)->default_value(test),
            "test enum class as input.");

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, config), vm);
    po::notify(vm);
    if (vm.count("help")) {
        std::cout << config << "\n";
        exit(1);
    }
    return test;
}

int main(int argc, char* argv[]) {
    std::cout << "Command line input: " << simple_options(argc, argv) << "\n";

    std::cout << "input bar or foo" << std::endl;
    if (test_enum_t test; std::cin >> test) {
        std::cout << test << std::endl;
    } else {
        std::cerr << "not a valid value";
        return 1;
    }
}

Печать

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -lboost_program_options
./a.out -i bar <<<foo
Command line input: bar
input bar or foo
foo
...