@azaznioo

Как передать и проверить много параметров функции?

Здравствуйте, есть функции в которые нужно передать много параметров. В разных функциях параметры могут изменятся, но не сильно (некоторых полей не будет или добавятся другие). Параметры я решил передавать через структуру, в которой поля и есть параметры функций. Необходимо все эти параметры предварительно проверить перед тем как их передавать. Проблема в том, что параметры у разных функций сильно совпадают, но не одинаковы. Мне не хочется дублировать код(описывать структуру и проверку) для каждой функции. Есть ли какой-то паттерн или способ это дело красиво обойти?
  • Вопрос задан
  • 691 просмотр
Решения вопроса 2
ManWithBear
@ManWithBear
Swift Adept, Prague
Обернуть всё в класс. С методом проверки. Все остальные наследуют от него/друг от друга.
Ответ написан
Комментировать
@monah_tuk
Дополню камрада ManWithBear

Не нужно структуру отдельно переделывать в класс. Это и так класс, только доступ по-умолчанию public. И метод проверки можно не добавлять, а сделать внешним по отношению к структуре. Свободные функции не всегда плохо. Плюсы такого подхода в том, что memory-layout будет предсказуемый, а если в структуре нет non-POD типов то они останутся POD типами.

Т.е. получится что-то вроде такого:
#include <iostream>
#include <iomanip>
using namespace std;

struct Point
{
  int x;
  int y;
};
inline bool is_valid(const Point& p)
{
  bool result = true;
  // some checks
  return result;
}

struct Rect : Point
{
  int width;
  int height;
};
inline bool is_valid(const Rect& r)
{
  bool result = is_valid(static_cast<const Point&>(r)) && r.width > 0 && r.height > 0;
  return result;
}

int main() {
	// теряется возможность делать так:
	//Rect r = {0, 1, 2, 3};
	Rect r{};
	r.x = 0;
	r.y = 1;
	r.width  = 10;
	r.height = 11;
	
	auto check = is_valid(r);
	
	cout << " x=" << r.x
	     << " y=" << r.y
	     << " w=" << r.width
	     << " h=" << r.height
	     << " valid=" << check
	     << endl;
	
	cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
	
	uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
	auto beg = ptr;
	auto end = ptr + sizeof(Rect);
	for (auto it = beg; it != end; ++it) {
		cout << hex << setfill('0') << setw(2) << (int)*it << " ";
	}
	cout << endl;
	
	return 0;
}


Можно функцию проверки сделать и методом класса/структуры и, при этом, не виртуальной. Тогда проверка будет вызываться только в соответствии с типом. Тогда можно обойтись без static_cast, получится что-то вроде:
#include <iostream>
#include <iomanip>
using namespace std;

struct Point
{
  int x;
  int y;
  
  bool is_valid() const
  {
  	bool result = true;
  	// some checks
  	return result;
  }
};

struct Rect : Point
{
  int width;
  int height;

  bool is_valid() const
  {
  	bool result = Point::is_valid() && width > 0 && height > 0;
  	return result;
  }
};

void pass_point(const Point &p)
{
	cout << "point is valid: " << p.is_valid() << endl;
}

int main() {
	// теряется возможность делать так:
	//Rect r = {0, 1, 2, 3};
	Rect r{};
	r.x = 0;
	r.y = 1;
	r.width  = 0; // make invalid
	r.height = 11;
	
	auto check = r.is_valid();
	
	cout << " x=" << r.x
	     << " y=" << r.y
	     << " w=" << r.width
	     << " h=" << r.height
	     << " valid=" << check
	     << endl;
    
    // но следующий вызов скажет что точка валидна:
    // вызов валиден, т.к. Rect отнаследован от Point
    pass_point(r);
	
	cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
	
	uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
	auto beg = ptr;
	auto end = ptr + sizeof(Rect);
	for (auto it = beg; it != end; ++it) {
		cout << hex << setfill('0') << setw(2) << (int)*it << " ";
	}
	cout << endl;
	
	return 0;
}


Сложности могут возникнуть, когда появятся ромбические связи и потребуется виртуальное наследование.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы