Я недавно читал о дизайне по контракту, и я хотел бы попробовать его в проекте. Первое, что пришло мне в голову, когда я попытался найти какое-то простое решение самостоятельно, было это:
#include <iostream>
using namespace std;
template <typename Type>
class Positive {
Type value;
public:
Positive(
Type positive
) :
value(positive)
{
if (!condition())
errorAction();
}
Positive(
const Positive& positive
) :
value(positive.value)
{} // no need to check since variable HAS to respect the contract
Positive(
const Positive&& positive
) :
value(positive.value)
{} // no need to check since variable HAS to respect the contract
Positive& operator=(
const Positive& positive
) {
value = positive.value;
return *this;
}
Positive& operator=(
const Positive&& positive
) {
value = positive.value;
return *this;
}
operator Type() {
return value;
}
private:
bool condition() {
return value > 0;
}
void errorAction() {
cout << "not positive value: " << value << endl;
exit(-1);
}
};
// contract: both inputs and output are positive
template <typename Type>
Positive<Type> minusPositive(
Positive<Type> x,
Positive<Type> y
) {
return x - y;
}
int main() {
int x = 10;
int y = 20;
int z = minusPositive<Type>(x, y); // should fails since x-y <= 0
cout << "Should never display this: " << z << endl;
return 0;
}
Он в основном использует неявное преобразование (конструктор) для инициации оболочки, ответственной за проверку контракта, и явный оператор преобразования для его возврата. Я думал, что это будет работать довольно хорошо — аргументы будут автоматически упакованы и проверены, а внутри функций это автоматически развернет содержимое. Аналогично с возвращаемыми аргументами. Мне нравится идея, так как контракт будет определен и проверен только в одном месте (аргумент / возвращаемое значение), а тело метода будет свободно от дополнительных строк.
Но затем я подумал обо всех тех проблемах, которые могли бы возникнуть — мне, скорее всего, пришлось бы перегружать = оператор и возможный оператор перемещения, сам код был бы не очень гибким (каждый контракт должен иметь свою собственную отдельную реализацию, и я не уверен, что шаблон поможет), не говоря уже о проблеме с провалом контракта внутри конструктора (как мне с ним работать? Не проверять контракт внутри него или не завершать программу на месте?).
Я проверил несколько статей об этом:
Знаете ли вы какой-либо подход / библиотеку, которая привела бы к чистому, читабельному коду, который не требует больших дополнительных усилий при определении каждого нового контракта? Что-то стабильное и зрелое, желательно уже доступное в Debian 7.3 без компиляции множества внешних библиотек или добавления нескольких новых репозиториев / закреплений?
В качестве альтернативы, вы думаете, что подход, который я придумал сначала, мог бы быть зафиксирован до такой степени, что он был бы действительно полезен?
РЕДАКТИРОВАТЬ
Чтобы этот вопрос не был не по теме: Было бы безопасно использовать классы, определенные аналогично определенным контрактам? Будут ли какие-то подводные камни, и, если таковые имеются, можно ли избежать того, как я буду использовать эти классы или как я их реализовал?
Задача ещё не решена.
Других решений пока нет …