Reputation: 30128
I have a (legacy) c interface looking something like this:
// c interface
#include <stdint.h>
#include <string.h>
typedef enum {
type_1 = 0, /* native type: uint8_t */
type_2 = 1, /* native type: double */
// ... lots of more types defined ...
} thing_type_t;
void thing_set(thing_type_t type, size_t type_size, void* value);
void thing_get(thing_type_t type, size_t type_size, void* return_value);
void thing_set_type_1(uint8_t value);
void thing_get_type_1(uint8_t *value);
void thing_set_type_2(double value);
void thing_get_type_2(double *value);
// ...
So basically you can set thing, and based on which thing_type_t
you choose there is a corresponding native type. This interface I cannot change.
Now I want to do a C++ interface utilising type polymorphism, so I from a client can do something like:
// C++ client
#include <cstdint>
int main(void)
{
Thing thing;
thing.set(std::uint8_t(42));
thing.set(42.0);
auto uivalue = thing.get<std::uint8_t>();
auto dvalue = thing.get<double>();
return 0;
}
It does not have to be exactly like this, but the idea is that the client do not have to worry about the internal thing_type_t
types, but just uses the corresponding native types.
What I have come up with is using templates like this:
// C++ interface
#include <cstdint>
class Thing
{
public:
template <typename T> void set(T value);
template <typename T> T get();
};
template <> void Thing::set(uint8_t value)
{
thing_set(type_1, sizeof value, reinterpret_cast<void*>(&value));
}
template <> std::uint8_t Thing::get()
{
uint8_t ret = 0;
thing_get(type_1, sizeof ret, &ret);
return ret;
}
template <> void Thing::set(double value)
{
thing_set(type_2, sizeof value, reinterpret_cast<void*>(&value));
}
template <> double Thing::get()
{
double ret = 0;
thing_get(type_2, sizeof ret, &ret);
return ret;
}
I.e. I am doing type specialisation for each of the thing_type_t
which results in bloated code with lots and lots of repeated code.
How do I simplify this so I can express the mapping between thing_type_t
and native type without duplicating the get/set function again and again?
I feel like I should be able somehow parameterize the get and set functions with both the thing_type_t
and native type as well as the mapping. But I cannot figure out how to do that.
Upvotes: 3
Views: 59
Reputation: 36498
As the only thing that changes between implementations is the first argument I'd just specialise that part:
class Thing
{
public:
template <typename T> void set(T value)
{
thing_set(getType<T>(value), sizeof value, reinterpret_cast<void*>(&value));
}
template <typename T> T get()
{
T ret = 0;
thing_get(getType<T>(ret), sizeof ret, &ret);
return ret;
}
private:
template <typename T> thing_type_t getType(T);
};
template <> thing_type_t Thing::getType(uint8_t)
{
return type_1;
}
template <> thing_type_t Thing::getType(double)
{
return type_2;
}
Upvotes: 5
Reputation: 746
macroses can help in such cases:
macro(type_1, std::uint8_t)
class Thing
{
public:
template <typename T> void set(T value);
template <typename T> T get();
};
#define macro(thing_type, actual_type) template <> void Thing::set(actual_type value) \
{\
thing_set(thing_type, sizeof value, reinterpret_cast<void*>(&value));\
}\
#include <typemap.h>
#undef macro
#define macro(thing_type, actual_type) template <> actual_type Thing::get()\
{\
actual_type ret = 0;\
thing_get(thing_type, sizeof ret, &ret);\
return ret;\
}\
#include <typemap.h>
#undef macro
so in the end you only have to modify typemap.h to add/remove specializations
Upvotes: 0