Как вернуть необязательную отладочную информацию из метода?

Допустим, у меня есть статический метод, который сравнивает два объекта для близкого соответствия и возвращает некоторый уровень достоверности [0,1].

class Foo
{
...
static double Compare(const Foo& foo1, const Foo& foo2);
...
};

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

Я вижу как минимум три варианта:

1: Создайте дополнительный класс CompareResult и сохраните там доверие + дополнительную информацию. Не заполняйте дополнительную информацию, если вам не нужно.

class CompareResult
{
...
private:
double confidence_;
CompareOptionalInfo compare_optional_info_;
...
};

...

static CompareResult Compare(const Foo& foo1, const Foo& foo2);

Кажется, он самый чистый, но я не уверен, стоит ли мне комбинировать возвращаемый результат с дополнительной информацией.

2: Используйте выходную переменную (таким образом, нам не нужно создавать дополнительный класс, но сигнатура нашего метода немного вырастет)

static double Compare(const Foo& foo1, const Foo& foo2, CompareOptionalInfo* out_compare_info = nullptr);

3: Отдельный метод сравнения с методом извлечения необязательной информации.

static double Compare(const Foo& foo1, const Foo& foo2);
static CompareOptionalInfo GetCompareOptionalInfo();

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

Исходя из вашего опыта, каков подходящий способ в мире ООП возвращать необязательную информацию (которая будет в основном использоваться только в режиме отладки) из метода?

2

Решение

Вариант 3 это не очень хорошая идея: наличие функций, зависящих от статических данных, не практично и может даже стать источником ошибок при отладке. Такая конструкция, кроме того, не является поточно-ориентированной; Как жаль, что такое ограничение будет создано только для целей отладки!

Примеры проблем:

double closest = std::max (Foo::compare (x, y), Foo::compare (y,z));
clog << Foo::GetCompareOptionalInfo();  // undefined which info since order of eval
// of function parameter is not guaranteed

double d = Foo::compare (x, y);
DoSomething();                    // what if this one does an unexpected  compare ?
clog << Foo::GetCompareOptionalInfo();

Вариант 2 Это жизнеспособное решение, но оно не очень удобно: оно заставляет вас создавать информационные объекты, передавать их по адресу и т. д .:

Foo::OptionalCompareInfo o1,o2;    // cumbersome
double closest = std::max (Foo::compare (x, y, &o1), Foo::compare (y,z, &o2));

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


Опция 1 отлично! Действуй ! Это действительно использует преимущества парадигмы ООП и использует чистый дизайн. Это практично и не накладывает ограничений на ваш код для его использования.

Все, что вам нужно, это предоставить некоторую (неявную) функцию преобразования для использования вашего CompareResult почти как если бы это было double:

class CompareResult
{
public:
CompareResult(double d=0.0) : confidence_(d) {};
operator double() { return confidence_; }
operator bool() { return confidence_>0.5; }
private:
double confidence_;
CompareOptionalInfo compare_optional_info_;
};

онлайн демо

Ваш производственный код не будет затронут отладочной информацией. И вы можете в своей отладке ВСЕГДА отследить объяснение любого результата сравнения, по крайней мере, если вы сохраните его:

Пример:

auto result = Foo::compare (x, y)
if (result) ... // uses automatically the conversion

auto closest = std::max (Foo::compare (x, y), Foo::compare (y,z));
// here you not only know the closest match but could explain it !

vector<CompareResult> v;
... // populate the vector with 10 000 comparison results
auto r = std::max_element(v.begin(), v.end());
// you could still explain the compare info for the largest value
// if you're interested in the details of this single value
// how would you do this with option 3 or option 2 ?

Хорошо, чтобы последний работал, вам также понадобится оператор сравнения для вашего дополнительного класса. Но это еще одна строка кода (см. Онлайн-демонстрацию) 😉

Наконец, может оказаться, что ваша «дополнительная информация отладки» может оказаться более полезной, чем ожидалось, например, предоставить пользователю дополнительные объяснения по запросу. Все, что вам нужно сделать, это удалить условные #ifdef DEBUG вокруг расчета дополнительной информации.

1

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

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

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

class Foo
{
private:
#ifndef NDEBUG
CompareOptionalInfo debug_out_compare_info_;
#endif

...
static double Compare(const Foo& foo1, const Foo& foo2,
CompareOptionalInfo* out_compare_info = nullptr);
...
};

#ifndef NDEBUG
CompareOptionalInfo Foo::debug_out_compare_info_;
#endif

В gdb в любой точке останова вы можете использовать:

call Foo::Compare(foo1, foo2, &Foo::debug_out_compare_info_);
print Foo::debug_out_compare_info_. ...
0

По вопросам рекламы [email protected]