У меня есть код C, где мне нужно делать некоторые вычисления с массивом данных. Данные могут быть либо INT, либо DOUBLE. Чтобы иметь дело с разными типами данных, я думал использовать оператор if/else
и определять указатель, содержащий данные внутри этого оператора:
/* put values into M, depending on data type*/
if (data_type == 2)
{
double *M;
M = somefunction(DOUBLE);
} else {
unsigned int *M;
M = somefunction(UINT16);
}
/* dummy code - usually I do some calculations on M which are data type independent */
for (i=0;i<(10);i++) {
M[i]=0;
}
Это приводит к проблемным задачам, поскольку M
не определяется вне конструкции if/else
:
error: ‘M undeclared (first use in this function)
Если я двигаю определение M
вне if/else
выражение, код скомпилируется, но M
внутри if/else
является другой M
снаружи.
Поэтому я могу обойти проблему, указав два указателя, один двойной и один int и проверим всюду в моем коде, с которым я имею дело:
double *Mdouble;
unsigned int *Mint;
/* put values into M, depending on data type*/
if (data_type == 2)
{
Mdouble = somefunction(DOUBLE);
} else {
Mint = somefunction(UINT16);
}
/* dummy code - usually I do some calculations on M which are data type independent */
for (i=0;i<(10);i++) {
if (data_type == 2) {
Mdouble[i]=0;
} else {
Mint[i]=0;
}
}
Итак, вот мой вопрос:
Как я могу решить эту проблему, когда M
является двойным или int, в зависимости от моих входящих данных? Могу ли я решить это с помощью какого-то указателя на работу указателя? Я не хочу писать дубликат кода для каждого случая.
EDIT может создавать шаблонные функции или перегружать функции, решая мою проблему? Я гибко отношусь к конкретному решению C/C++
Вам придется писать дубликат кода.
На базовом уровне требуемый машинный код для добавления двух чисел отличается для целых чисел и чисел с плавающей запятой.
Это различие "скрыто", поскольку данные печатаются, поэтому компилятор всегда знает тип каждого операнда и может генерировать правильный код.
Если вы хотите переместить эту информацию до времени выполнения, компилятор больше не сможет выполнить свою задачу, поэтому вам придется делать это вместо этого и убедитесь, что ваш код имеет правильный путь.
Вы можете попробовать использовать C-соединение:
#include <string.h>
#include <stdio.h>
int main(int argc, char** argv) {
union MyUnion {
int I;
double D;
};
union MyUnion M[10];
// Initializing the array to 0 (data type independent)
memset(M, 0, 10*sizeof(MyUnion));
M[0].I = 1;
M[1].D = 1.5;
if (argc==1) { // It should be "if (data_type==2) {"
// but I wanted an example that compiles easily
printf("%i\n", M[0].I); //somefunction(M[0].I);
} else {
printf("%lf\n", M[1].D); //somefunction(M[1].D);
}
}
В C вам нужно будет использовать макрос
int8_t* ptr=...;
while(n)
{
switch(data_type)
{
case TYPE_DOUBLE:
A_MACRO_THAT_DEFINES_YOUR_OPERATION((double*)ptr);
ptr+=sizeof(double);
break;
case TYPE_INT:
A_MACRO_THAT_DEFINES_YOUR_OPERATION((int*)ptr);
ptr+=sizeof(int);
break;
}
--n;
}
Это решение выполняется медленно, так как для каждого элемента необходимо проверить тип данных. Вместо этого вы можете написать весь цикл как макрос. Это будет быстрее, но сложнее читать.
Если вы можете использовать C++, используйте вместо этого шаблон. Вот решение с использованием шаблонов:
template<class T>
void doStuff(T* ptr_begin,T* end)
{
while(ptr_begin!=ptr_end)
{
// Do stuff
++ptr_begin;
}
}
void doStuffWrapper(void* ptr_begin,void* ptr_end,uint32_t type)
{
switch(type)
{
case TYPE_DOUBLE:
doStuff((double*)ptr_begin,(double*)ptr_end);
break;
case TYPE_INT:
doStuff((int*)ptr_begin,(int*)ptr_end);
break;
}
}
В качестве побочного примечания: в этом случае я предпочитаю switch-case over if-else, так как он легче поддерживать и может генерировать более быстрый код.
Похоже, вы хотите использовать шаблонную функцию.
Но я думаю, что вопрос сводится к тому, что вы можете поместить всю свою работу в эту функцию?
template <typename T>
T* somefunction(T data){
//start old somefunction code
T* result = new T[10]{data, data, data, data, data, data, data, data, data, data};
//end old somefunction code
//begin "calculations on M which are data type independent"
for(int i = 0; i < 10; i++){
M[i] = 0;
}
return M;
}
Таким образом, с приведенным выше кодом вы можете сделать либо double* Mdouble = somefunction(13.13)
либо short* Mshort = somefunction(13);
Наилучший сценарий - это то, что M
действительно было отброшенным массивом в любом случае, и в этом случае вы могли бы даже распределить данные в фрейме стека функций шаблона и избегать использования new
.
Вы можете использовать макрос, например:
#define CALCULATIONS(M) do { \
for (i=0;i<(10);i++) \
(M)[i]=0; \
} while (0)
а затем в другом коде:
if ( data_type == 2 )
{
double *M = whatever;
CALCULATIONS(M);
}
Смотрите здесь, если вы не знакомы с do...while(0)
while (0)
необходима? {
отсутствует в строке 2?
somefunction
шаблона, которая может решить вашу проблему.