The scenario: I want to read a file which contains value with a data type defined in its header, copy its content to a temporary image, modify the content of the temporary image and save it again.
The problem is that the size of the data type requires different ways of accessing/modifying the content and results in large switch statements due to different data types (the list here is shortened). Also the type is only known at runtime, not at compile-time.
#include <stdint.h>
#include <stdlib.h>
typedef enum {
DT_UCHAR, /** @brief Datatype 'unsigned char' */
DT_CHAR, /** @brief Datatype 'signed char' */
DT_USHORT, /** @brief Datatype 'unsigned short' */
DT_SHORT, /** @brief Datatype 'signed short' */
DT_UINT, /** @brief Datatype 'unsigned int' */
DT_INT, /** @brief Datatype 'signed int' */
DT_FLOAT, /** @brief Datatype 'float' */
DT_DOUBLE, /** @brief Datatype 'double' */
} e_datatype;
struct image {
e_datatype type;
size_t size;
void* data;
};
image image1;
image image2;
image image3;
template <typename T>
void create_mask(void *const dst, const unsigned i_dst, const void *const src, const unsigned i_src, const void* const threshold) {
if (static_cast<const T*>(src) < static_cast<const T*>(threshold))
*(static_cast<T*>(dst)+i_dst) = 1;
}
void create_mask(image *const out, const unsigned out_i, const image *const in, const unsigned in_i, const image* const threshold) {
if (in->type != threshold->type || out->type != DT_UCHAR)
return;
switch (out->type) {
case DT_UCHAR:
create_mask<unsigned char>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_CHAR:
create_mask<signed char>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_USHORT:
create_mask<unsigned short>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_SHORT:
create_mask<signed short>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_UINT:
create_mask<unsigned int>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_INT:
create_mask<signed int>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_FLOAT:
create_mask<float>(out->data, out_i, in->data, in_i, threshold->data);
break;
case DT_DOUBLE:
create_mask<double>(out->data, out_i, in->data, in_i, threshold->data);
break;
default:
//printf("ERROR. Unknown type.\n");
break;
}
}
size_t sizeof_image(e_datatype type) { return 1 /* another switch with the size of each datatype */; }
void read_image() {
image *my_image1 = &image1;
image *my_image2 = &image2;
image *threshold = &image3;
// read header and save it in my_image and then
// read data and copy it to the data field of my_image
read_image_header("mydata_uint.dat", my_image1);
my_image1->data = calloc(my_image1->size, sizeof_image(my_image1->type));
read_image_data("mydata_uint.dat", my_image1);
// create output mask
my_image2->size = my_image1->size;
my_image2->type = DT_UCHAR;
my_image2->data = calloc(my_image2->size, sizeof_image(DT_UCHAR));
// read threshold value from another image
read_image_header("mydata_thresh.dat", threshold);
threshold->data = calloc(threshold->size, sizeof_image(threshold->type));
read_image_data("mydata_thresh.dat", threshold);
for (unsigned i = 0; i < my_image1->size; i++)
create_mask(my_image1, i, my_image2, i, threshold);
}
Is it possible to rewrite the image class/struct such that it would use a template class with the datatype being set at inside read_image()
? Hence reducing the amount of switch statements.
My limitation is that I cannot use C++ Standard Library features and I am limited to C++03.
I found a solution with function pointers but this solution does not seem shorter than those large switch statements.