Как защититься от аргументов функции, передаваемых в неправильном порядке?

Скажем, у меня есть функция C ++, которая выглядит так:

double myfunction(double a, double b) {
// do something
}

Который я тогда называю так:

double a = 1.0;
double b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a); // compiles fine

Я хотел бы убедиться, что a а также b никогда не предоставляются в неправильном порядке.
Каков наилучший способ обеспечить это в C ++?

Другие языки допускают именованные параметры, например:

double good_r = myfunction(a=a, b=b);
double bad_r = myfunction(a=b, b=a); // mistake immediately obvious
double bad_r = myfunction(b=b, a=a); // compiles fine

Или, возможно, проблему можно частично решить с помощью типов, то есть

double my_type_safe_function(a_type a, b_type b) {
// do something
}
a_type a = 1.0;
b_type b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a); // compilation error

РЕДАКТИРОВАТЬ: Несколько человек спросили, что я имею в виду под «неправильный порядок». Я имею в виду, что в реальном коде a а также b иметь какое-то значение. Например, аргументы могут вместо height а также width, Разница между ними очень важна для того, чтобы функция возвращала правильный результат. Однако они оба являются поплавками и имеют одинаковые размеры (то есть длину). Кроме того, нет «очевидного» порядка для них. Человек, пишущий объявление функции, может принять (width, height) и человек, использующий функцию, может предположить, (height, width), Я бы хотел, чтобы это не произошло по ошибке. С двумя параметрами легко быть осторожным с заказом, но в большом проекте и с количеством аргументов до 6 аргументов закрадываются ошибки.

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

4

Решение

Как насчет этого:

struct typeAB {float a; float b; };

double myfunction(typeAB p) {
// do something
return p.a - p.b;
}

int main()
{
typeAB param;
param.a = 1.0;
param.b = 2.0;
float result = myfunction(param);
return 0;
}

Конечно, вы можете все еще испортить, когда назначаете свои параметры, но этого риска трудно избежать 🙂

3

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

Вариант состоит в том, чтобы иметь одну структуру на «новый» тип, а затем заставить их уйти в оптимизированных сборках с использованием макросов.

Что-то вроде этого (только слегка проверено, так что это может быть далеко):

#define SAFE 0

#if SAFE
#define NEWTYPE(name, type) \
struct name { \
type x; \
explicit name(type x_) : x(x_) {}\
operator type() const { return x; }\
}
#else
#define NEWTYPE(name, type) typedef type name
#endif

NEWTYPE(Width, double);
NEWTYPE(Height, double);

double area(Width w, Height h)
{
return w * h;
}

int main()
{
cout << area(Width(10), Height(20)) << endl;

// This line says 'Could not convert from Height to Width' in g++ if SAFE is on.
cout << area(Height(10), Width(20)) << endl;
}
2

Я думаю, что вы уже предоставили самое простое решение, используя типы.

Одной из альтернатив может быть использование класса построителя и цепочки методов.
Подобно:

class MyfunctionBuilder {
MyFunctionBuilder & paramA(double value);
MyFunctionBuilder & paramB(double value);
double execute();
(...)
}

Который вы бы использовали так:

double good_r = MyFunctionBuilder().paramA(a).paramB(b).execute();

Но это много лишнего кода для написания!

1

Что такое «неправильный порядок» на самом деле? В этом вашем примере

double myfunction(double a, double b) {
// do something
}

double a = 1.0;
double b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a);

как вы на самом деле хотите знать, если это является правильный порядок? Что если переменные будут называться «quapr» и «moo» вместо «a» и «b»? Тогда было бы невозможно угадать, является ли порядок правильным или неправильным, просто взглянув на них.

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

float getTax( float price, float taxPercentage )

вместо

float getTax( float a, float b )

Во-вторых, выполните необходимые проверки внутри:

float divide( float dividend, float divisor )
{
if( divisor == 0 )
{
throw "omg!";
}
}

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

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