Я делаю интерфейс протокола. Этот протокол должен быть сервером для отправки данных АЦП с устройства. Различные микроконтроллеры имеют библиотеки, которые позволяют читать АЦП, но, разумеется, они разные.
Например, аналоговое чтение в Nucleo:
AnalogIn ain(A0);
unsigned short value = ain.read_u16();
И Ардуино:
int analogPin = 3;
int val = analogRead(analogPin);
Хотя эти методы возвращают разные типы и на самом деле совершенно разные типы, они возвращают одни и те же данные.
Мой вопрос заключается в том, можно ли сделать класс, который принимает обе эти функции в качестве обратного вызова и возвращает их возвращаемое значение как int
.
Как это:
class ThisIsNotReal {
public:
ThisIsNotReal(<ANY TYPE THAT CAN CONVERT TO INT HERE> (*cb)() ) {
callback = cb;
}
int getVal() {
return callback();
}
private:
<ANY TYPE ... (some super magical template? anyone?)> (*cb)()
}
Конечно, моя цель - иметь совместимый с Arduino код без написания слишком большого кода. Кроме того, я хочу изучить магию шаблонов.
Если у вас нет компилятора С++ 11 для всех целевых устройств с использованием обратного вызова, это не обязательно будет просто. Это связано с различиями между требованиями к хранению и аргументами, необходимыми для вызова различных функций. Потому что если вы можете подумать о создании общего интерфейса для чтения ввода с порта ввода-вывода, а затем предоставить уникальную реализацию для каждого целевого устройства/платформы. Это позволит вам иметь общий метод инициализации объекта, связанного с IO, а также чтение из порта ввода-вывода.
Даже если у вас есть компилятор С++ 11 для ваших целевых платформ, использование std::function
или virtual
функций может быть нежелательным (или даже вариантом), если они окажут влияние на производительность. Что-то вроде следующего кода может быть более жизнеспособным, так как это просто.
#if defined (IS_NUCLEO)
struct InputDevice
{
InputDevice(int port) : device(port) {}
int read() const
{
return static_cast<int>(device.read_u16());
}
AnalogIn device;
};
#endif
#if defined(IS_ARDUINO)
struct InputDevice
{
InputDevice(int port) : port(port) {}
int read() const { return analogRead(port); }
int port;
};
#endif
Я предлагаю вам адаптировать его для удовлетворения ваших точных требований.
кажется, что std::function
и lambda могут помочь, например:
AnalogIn ain(A0);
std::function<int()> AdcRead = [ain](){ return ain.read_u16(); };
А также
const int analogPin = 3;
std::function<int()> AdcRead = [analogPin](){ return analogRead(analogPin); };
ThisIsNotReal
должен хранить указатель типа ConvertFuncToIntBase* conv
и вызывать conv->getVal()
. Вы создадите соответствующий экземпляр, вызвав makeIntConverter
в конструкторе ThisIsNotReal
. makeIntConverter
не будет компилироваться, если возвращаемый тип переданной ему функции не конвертируется в int
. Хотя нижеприведенное решение с использованием std::enable_if
и std::is_convertible
, для которого требуется С++ 11, вы можете использовать аналогичные методы Boost для С++ 03.
class ConvertFuncToIntBase
{
public:
virtual int getVal() = 0;
virtual ~ConvertFuncToIntBase(){}
};
template <typename T>
class ConvertFuncToInt : ConvertFuncToIntBase
{
public:
ConvertFuncToIntLand(T (*cb)()) : callback(cb)
{
}
int getVal()
{
return (int)callback();
}
private:
T (*callback)();
};
template <typename T, typename = typename std::enable_if<std::is_convertible<T, int>>::type>
std::unique_ptr<ConvertFuncToIntBase> makeIntConverter(T (*cb)())
{
return std::unique_ptr<ConvertFuncToIntBase>(new ConvertFuncToInt<T>(cb));
}
callback
не определен, ConvertFuncToInt
наследует (конфиденциально) от самого себя, ConvertFuncToIntLand
не является конструктором, нет виртуального деструктора для базового класса, отсутствует указатель для makeIntConverter
(тогда как unique_ptr
будет лучше).
int val = static_cast<int>(ain.read_u16());
?