Измените семантику значений в boost :: program_options после add_options (). То есть значение по умолчанию

Я наткнулся на проблему, определенную в названии. У меня есть приложение, которое создает экземпляр options_description затем использует add_options() в теме.
Совсем как в примере:

options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;

У меня вопрос, как можно изменить значение по умолчанию для optimization после этого звонка. Это вообще возможно? Документация кажется мне довольно мутной. Из того, что я понимаю, вопрос можно обобщить на любое значение семантического, как value_semantic является вторым параметром в скобках.

мотивация

У меня такое чувство, что это может быть невозможно. Поэтому я хотел бы представить свою мотивацию для такой функциональности. Может быть, мой намеренный дизайн имеет недостатки, так что вы можете предложить что-то еще.

У меня есть несколько программ, которые выполняют довольно похожие задачи и имеют несколько общих параметров и параметров. Я думал, что смогу реорганизовать общие параметры в отдельный базовый класс. Я подумал, что могу реорганизовать разбор командной строки таким же образом. boost::program_options работаю довольно хорошо с моей идеей. Я строю options_description Экземпляр как частное свойство в базовом классе и добавить общие параметры там. Затем в производных классах при инициализации я выполняю add_options() на этот объект снова добавляются более конкретные параметры. Это казалось довольно опрятным, и я получил его довольно быстро.

Затем я заметил, что все производные классы будут иметь общую опцию, но было бы неплохо иметь другое значение по умолчанию. То есть имя выходного файла. Для app1 должно быть app1.out, app2 — app2.out и т. Д.

Конечно, я могу переместить параметр выходного файла в add_options в производных классах, но это кажется глупым и избыточным, так как даже семантически все одинаково, кроме значения по умолчанию. Другим обходным решением было бы отказаться от значения по умолчанию в базовом классе и на этапе после анализа в производных классах проверить, была ли установлена ​​опция, и применить (по умолчанию) значение вручную. Однако это также кажется избыточным, поскольку предполагаемая функциональность, кажется, реализована в самой библиотеке.

Я постараюсь предоставить пример кода, чтобы вы могли почувствовать его позже или по запросу. Хотя я думаю, что мой подход довольно ясен.

РЕДАКТИРОВАТЬ — Примеры кода
Это было написано после ответа Роба, поэтому я старался придерживаться соглашения об именах.

Base — выполняет синтаксический анализ и позволяет задать уровень оптимизации как целое число:

#include <boost/program_options.hpp>
namespace po = boost::program_options;

class BaseClass {
public:
BaseClass::BaseClass();
virtual int parse(const int argc, char** argv);
private:
po::options_description m_desc;
po::variables_map vm;
int optimization_level;
};

BaseClass::BaseClass():
m_desc()
{
m_desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;
}

int BaseClass::parse(const int argc, char** argv)
{
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) { std::cout << desc << "\n"; return 1; }
optimization_level = vm["optimization"].as<int>();
return 0;
}

Высокооптимизированная версия, которая позволяет дополнительно выполнять модные вещи:

class HighlyOptimizedClass : public BaseClass {
public:
HighlyOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
bool fancy_optimizations;
};

HighlyOptimizedClass(): BaseClass() {
m_desc.add_options()
("fancy,f", po::value<bool>()->zero_tokens(), "perform fancy optimizations")
;
}

HighlyOptimizedClass::parse(const int argc, char** argv)
{
int ret = BaseClass::parse(argc, argv);      //execute base function
if( ret ) return ret;                        //return if it didnt succed
if ( vm.count("fancy") ) fancy_optimizations = 1;  // non-base stuff
return 0;
}

Неоптимизированная версия, которая позволяет включить подробную отладку:

class NonOptimizedClass : public BaseClass {
public:
NonOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
bool verbose_debug;
};

NonOptimizedClass(): BaseClass() {
m_desc.add_options()
("verbose,v", po::value<bool>()->zero_tokens(), "genrates TONS of output")
;
}

NonOptimizedClass::parse(const int argc, char** argv)
{
int ret = BaseClass::parse(argc, argv);       // execute base function
if( ret ) return ret;                         // return if it didnt succed
if ( vm.count("verbose") ) verbose_debug = 1; // non-base stuff
return 0;
}

Я пытался сжать его вертикально, но в любом случае он стал длинным = /. Извините, если я пошел за борт. В свою очередь, примеры понятны и самодостаточны.

BaseClass настраивает почти все и разбирает обычные вещи. Производные классы добавляют свои собственные опции в конструкторы и анализируют перегрузку. Они выполняют базовый анализатор и проверяют наличие ошибок. Это также делает --help Работа.

Теперь дело в том, чтобы изменить значение оптимизации по умолчанию для каждого из производных. Как было бы неплохо, чтобы оно было установлено очень низким для NonOptimizedClass и действительно высоко для OptimizedClass,

2

Решение

Ты можешь позвонить options_description::find("optimization", ...) чтобы получить ссылку на связанный option_description, И его semantic метод даст вам указатель на value_semantic что вы изначально предоставили во время звонка add_options, Однако это константный указатель, поэтому вам не разрешено изменять то, на что он указывает.

Тем не менее value_semantic не был const, когда вы его создали, а это значит, что он должен быть безопасным в использовании const_cast удалить постоянную квалификацию, которая option_description применяется. Вам также придется набрать value_semantic Возврат вправо typed_value типа, что вы получили, когда вы первоначально позвонили value<T>,

option_description const& optimization = desc.find("optimization", false);
shared_ptr<const value_semantic> cvalue = optimization.semantic();
shared_ptr<value_semantic> value = const_pointer_cast<value_semantic>(cvalue);
shared_ptr<typed_value<int>> tvalue = dynamic_pointer_cast<typed_value<int>>(value);
assert(tvalue);
tvalue->default_value(20);

Альтернативный дизайн, позволяющий избежать необходимости изменять параметры после их определения (что явно не является чем-то program_options было разработано, чтобы делать), чтобы программный производный класс передавал желаемое значение по умолчанию в базовый класс. Тогда базовый класс может использовать это значение при определении опции оптимизации.

BaseClass::BaseClass(int default_optimization):
m_desc()
{
m_desc.add_options()
("help",
"produce help")
("optimization",
value<int>()->default_value(default_optimization),
"optimization level")
;
}

HighlyOptimizedClass::HighlyOptimizedClass():
BaseClass(99)
{ }

NonOptimizedClass::NonOptimizedClass():
BaseClass(0)
{ }
5

Другие решения

const std::vector< shared_ptr< option_description > > & options_description::options() const; дает вам право на запись в option_description,

const shared_ptr<T> это не shared_ptr<const T>,

Однако я бы вместо этого использовал add_output_file( std::string default )и позвони add_optionЕсли бы у меня была такая проблема. (вполне возможно, несмотря на то, что вышеприведенное выглядит правдоподобно для интерфейса, путаница с внутренними компонентами может запутать boost библиотека.

0

Итак, я понял, что в текущей версии невозможно изменить value_semantic без нарушения существующего дизайна program_options от const_castING.

Прочитав ответ и предложения Роба Кеннеди и Якка, я придумал подход, который является комбинацией двух. Он должен работать так, как описано, и не должен ничего загромождать без необходимости.

Идея состоит в том, чтобы добавить опцию, которая предназначена для изменения в отдельном вызове. Сделайте его виртуальным и определите регистр по умолчанию в базовом классе.

Такой подход позволяет настроить весь program_option сразу, а не только единый смысл. Добавление параметра или метода для каждого отдельного случая, который может быть изменен, кажется мне очень обременительным.

Измененные коды будут выглядеть так:

База

class BaseClass {
public:
BaseClass::BaseClass();
virtual int parse(const int argc, char** argv);
private:
virtual void add_optimization_option();
po::options_description m_desc;
po::variables_map vm;
int optimization_level;
};

BaseClass::BaseClass(): m_desc() {
m_desc.add_options()("help", "produce help");
}

void BaseClass::add_optimization_option(){
m_desc.add_options()
("optimization", value<int>()->default_value(10), "optimization level");
}

Оптимизированная версия:

class HighlyOptimizedClass : public BaseClass {
public:
HighlyOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
virtual void add_optimization_option();
bool fancy_optimizations;
};

void HighlyOptimizedClass::add_optimization_option(){
m_desc.add_options()
("optimization", value<int>()->default_value(99), "optimization level");
}

Неоптимизированные:

class NonOptimizedClass : public BaseClass {
public:
NonOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
virtual void add_optimization_option();
bool verbose_debug;
};

void NonOptimizedClass::add_optimization_option(){
m_desc.add_options()
("optimization", value<int>()->default_value(0), "optimization level");
}

Стоимость состоит в том, чтобы добавить один закрытый метод для каждой измененной опции и перегрузить один закрытый метод для каждого случая, когда мы хотим изменить его. Когда кто-то хочет оставить его по умолчанию, ничего не нужно.
Если модификация value_semantic было возможно определить новые методы можно было избежать. Тем не менее, кроме этого препятствия, оно работает хорошо и не загромождает ничего другого.

0