Reputation: 3
I'm writing a class for communicating sensor data and I want to make it so that a user of the class can't use an incorrect data type for sending sensor data.
I want to set up my class sort of like this:
private:
static const _sensor_bytes[] = {2, 4, ...};
public:
enum sensor_name_t {
SENSOR1 = 0,
SENSOR2 = 1,
...
};
template<class T>
void writeData(sensor_name_t sensor, T data){
if _sensor_bytes[sensor] != sizeof(T){
// Do not compile
}
}
So for example,
int data = 30;
writeData(SENSOR1, data); // This would not compile because _sensor_bytes[0] = 2 and sizeof(int) = 4
writeData(SENSOR2, data); // This would compile because _sensor_bytes[1] = 4 and sizeof(int) = 4
Am I going about this in the right way? And if so, how would I accomplish this functionality?
Upvotes: 0
Views: 186
Reputation: 136
Using size to check the data type may not be the best approach for the following reasons:
Different data types can have the same size, such as float and int.
Using a different datatype does not always imply that the value contained inside the variable is wrong. For example, in your case:
int data = 30; writeData(SENSOR1, data); // This would not compile because _sensor_bytes[0] = 2 and sizeof(int) = 4
the value 30 can be stored in 2 bytes, so do you really want to throw a compilation error here? However, that is purely implementation specific.
One way you can validate the type of template function argument at compile time is by initializing a dummy variable. Below is an example:
enum sensor_name_t {
SENSOR1 = 0,
SENSOR2 = 1
};
int sensor1;
std::string sensor2;
template<class T>
void writeData(sensor_name_t sensor, T data){
switch(sensor)
{
case SENSOR1:
{
int temp = data; // you know that sensor1 is of type int
// do your stuff
}
break;
case SENSOR2:
{
std::string temp = data; // you know that sensor2 is of type string
// do your stuff
}
break;
default:
break;
}
}
int main()
{
sensor1 = 30;
writeData(SENSOR1, sensor1);
writeData(SENSOR2, sensor1);
}
The above code snippet generates the following compile time error:
error: conversion from 'int' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string'} requested
The above logic can be implemented in a more elegant way. You can get some idea here
Upvotes: 0
Reputation: 1293
You can't do this with function void writeData(sensor_name_t sensor, T data)
, because value of sensor
is known at runtime, so it can't by validated in compile-time and rise an error.
Variant 1
You can try to use std::enable_if
, move sensor
from function parameter to template parameter and use constexpr
for _sensor_bytes
. It requires C++11 support by compiler.
#include <cstdint>
#include <type_traits>
class Test {
private:
static constexpr int _sensor_bytes[] = {2, 4};
public:
enum sensor_name_t {
SENSOR1 = 0,
SENSOR2 = 1
};
template<int sensor,
class T,
typename = typename std::enable_if<(sizeof(T) == _sensor_bytes[sensor])>::type >
void writeData(T data) {
// Use 'sensor' and 'data' here
// ...
}
};
int main()
{
Test test;
uint16_t data1;
uint32_t data2;
test.writeData<Test::SENSOR1>(data1);
test.writeData<Test::SENSOR2>(data2);
//test.writeData<Test::SENSOR2>(data1); // Will not compile
//test.writeData<Test::SENSOR1>(data2); // Will not compile
return 0;
}
Variant 2
If you still need both function parameters passed in runtime, then validation should be done also in runtime:
assert
Upvotes: 1