Вот код для чтения матрицы, обратите внимание, что у вас также есть функция csvwrite в matlab
void loadFromCSV( const std::string& filename )
{
std::ifstream file( filename.c_str() );
std::vector< std::vector<std::string> > matrix;
std::vector<std::string> row;
std::string line;
std::string cell;
while( file )
{
std::getline(file,line);
std::stringstream lineStream(line);
row.clear();
while( std::getline( lineStream, cell, ',' ) )
row.push_back( cell );
if( !row.empty() )
matrix.push_back( row );
}
for( int i=0; i<int(matrix.size()); i++ )
{
for( int j=0; j<int(matrix[i].size()); j++ )
std::cout << matrix[i][j] << " ";
std::cout << std::endl;
}
}
C11 поддерживает типовые выражения, которые должны делать то, что вы ищете:
#define Set_b(X, y) _Generic((X), A: Set_b_A,\
B: Set_b_B \
)((X), (y))
Затем просто реализовать Set_b_A
и Set_b_B
для соответствующих типов.
Прежде всего, рекомендуется передавать указатель на структуру, а не полную копию по значению.
Метод 1: Существует один метод, который широко используется в программировании ядра Linux, и это container_of
макрос. Если вы дадите ему указатель на элемент в качестве входных данных, он даст вам структуру, содержащую этот элемент. Вы можете сделать нечто подобное внутри функций set_b()
, set_c()
, set_d()
.
Метод 2: На примере set_b()
можно добавить еще 1 дополнительный аргумент, чтобы рассказать о типе структуры, а также сделать первый указатель void *
. Подпись станет:
set_b(void * str, int num, int str_type)
Вы можете использовать str_type
как 1
для struct A
и 0
для struct B
. Теперь внутри определения функции, вы должны проверить тип и снова переустановить указатель void
на правильный тип struct
Я бы сделал это так. Он отлаживаем и не генерирует слишком много кода (memcpy будет оптимизирован из реального кода на любом уровне оптимизации) https://godbolt.org/z/lMShik
Кстати в этом случае небезопасная версия IMO так же безопасна, как и IMO, она нарушает строгие правила псевдонимов.
typedef enum
{
TYPE_A,
TYPE_B,
TYPE_C
}Types;
struct a
{
int a;
int b;
int c;
int d;
};
struct b
{
int a;
int b;
int c;
};
struct c
{
int a;
int b;
};
void inline __attribute__((always_inline)) Set_a(void *ptr, Types type, int value)
{
struct a a;
struct b b;
struct c c;
switch(type)
{
case TYPE_A:
memcpy(&a, ptr, sizeof(a));
a.a = value;
memcpy(ptr, &a, sizeof(a));
break;
case TYPE_B:
memcpy(&b, ptr, sizeof(b));
b.a = value;
memcpy(ptr, &b, sizeof(b));
break;
case TYPE_C:
memcpy(&c, ptr, sizeof(c));
c.a = value;
memcpy(ptr, &c, sizeof(c));
break;
}
}
void inline __attribute__((always_inline)) Set_a_unsafe(void *ptr, Types type, int value)
{
struct a a;
struct b b;
struct c c;
switch(type)
{
case TYPE_A:
((struct a *)ptr) -> a = value;
break;
case TYPE_B:
((struct b *)ptr) -> a = value;
break;
case TYPE_C:
((struct c *)ptr) -> a = value;
break;
}
}
struct a x,y;
int main()
{
Set_a(&x, TYPE_A, 45);
Set_a_unsafe(&y, TYPE_B, 100);
printf("%d\n", x.a);
printf("%d\n", y.a);
}
Существует несколько различных альтернативных подходов, которые могут быть использованы, однако это зависит от того, чем вы владеете, и может отличаться от того, чем вы не владеете, и не может изменять или изменять. Реальная реализация может зависеть от того, как часто structA
используется с API по сравнению с structB
. Вы действительно не предоставляете достаточно информации для окончательного предлагаемого подхода.
Похоже, что ваша публикация может быть изменена следующим образом.
Есть две структуры, structA
и structB
, которые имеют общих членов. Эти общие члены хранят тот же тип данных, и при проверке вся structB
содержится в structA
, как в:
typedef struct {
int a;
int b; // beginning of portion that is same as structB below.
int c;
int d; // end of portion that is same as structB below.
int e;
} structA;
typedef struct {
int b; // same type of data as in b member of structA above
int c; // same type of data as in c member of structA above
int d; // same type of data as in d member of structA above
} structB;
Для конкретного примера, structA
описывает объект в некотором трехмерном пространство, местоположение которого является кортежем x, y, z, который указан в structA
членах b
, c
, d
и structB
, используется для хранения местоположения только как x, y, z кортеж. [одна тысяча сто восемьдесят-два]
У вас есть API, который работает с данными в structB
, и, поскольку те же данные находятся в structA
, вы столкнулись с проблемой, что у вас должен быть API, состоящий из набора функций, который дублируется, один версия API, которая принимает в качестве аргумента structB
, а другая принимает в качестве аргумента structA
.
Чтобы расширить наш конкретный пример объектов в трехмерном пространстве, у вас есть API, который содержит функцию translate()
, которая переводит координаты на некоторое расстояние. Поскольку у вас есть две разные структуры в соответствии с MISRA C, вам понадобятся две разные версии этой функции: translate_structA()
, которая принимает в качестве аргумента a structA
, и вторую translate_structB()
, которая принимает в качестве аргумента a structB
. ].
Итак, вы столкнулись с необходимостью написать две версии каждой функции в вашем API, и вы не хотите этого делать.
Альтернатива 1 - заменить клонированные элементы фактической структурой
Использовать хорошую разработку программного обеспечения и вместо использования этого типа данных structB
в structA
в качестве клонированного набора элементов, вместо этого замените этих клонированных членов на structB
.
typedef struct {
int b; // same type of data as in b member of structA above
int c; // same type of data as in c member of structA above
int d; // same type of data as in d member of structA above
} structB;
typedef struct {
int a;
structB xyz; // replace the cloned members with an actual structB
int e;
} structA;
Затем вы пишете API, который работает с structB
только в терминах structB
. В тех местах, где вы используете structA
, вы просто используете член xyz
в интерфейсе вызова функции.
Хорошая особенность этого подхода заключается в том, что при добавлении дополнительных новых типов данных, требующих structB
, вы просто добавляете элемент structB
вместо клонирования элементов, и API, использующий structB
, может быть используется с новыми типами данных.
Однако, чтобы воспользоваться этим подходом, вам необходимо владеть технологией и иметь возможность вносить подобные изменения. С другой стороны, это самая простая, простая и читаемая альтернатива из всех, которые я могу себе представить. Он также должен иметь неплохую эффективность во время выполнения.
Примечание о следующих двух альтернативах
Прежде чем перейти к следующим двум альтернативам, вы должны рассмотреть основной недостаток в обоих из них.
Если зависимость structA
от structB
не указана как вид контракта с использованием structB
в structA
, вы вводите своего рода логическое или когнитивное межмодульное соединение , в котором у вас есть общий компонент, который представляет собой сам исходный код, а не программный компонент, полученный из исходного кода.
Техническое обслуживание становится проблемой, потому что теперь две структуры должны быть изменены вместе. И если связь между этими двумя областями не задокументирована в исходном коде и самих определениях структуры, программист, плохо знакомый с кодом, вероятно, упустит это.
И если будут введены новые типы данных, использующие данные structB
, шаг клонирования необходимо будет сделать снова, и вы просто расширяете поверхность сложных связей.
Альтернатива 2 - сортировка в / из интерфейсного объекта
Если у вас нет контроля над структурами, то другой альтернативой является выполнение сортировки данных в structA
и из него в a structB
, а затем написать API только в терминах structB
. Тогда в любом месте, где structA
нужно использовать API, вы должны иметь маршаллинг или преобразование, в котором выбираются конкретные данные в structA
для создания временного structB
, который затем используется с функцией. Если функция изменяет данные в structB
, вам нужно будет скопировать данные из structB
обратно в structA
, прежде чем удалять временные.
В качестве альтернативы вы можете решить использовать API в терминах structA
с маршаллингом в тех случаях, когда вы хотите использовать structB
с API. Эта альтернатива может быть предпочтительнее, если большая часть API использует structA
, а лишь немногие используют structB
.
Есть несколько способов сделать этот подход маршаллинга, в основном определяемый тем, будут ли интерфейсы API возвращать измененный объект данных или нет.
Во-первых, должен иметь дублированный набор функций, которые вызываются с использованием structA
, и этот дублирующий набор функций обрабатывает маршалинг данных между временным structB
, который затем используется при вызове фактического API, который занимает structB
.
Так что-то вроде:
int funcThing (structB thing);
int funcThing_structA (structA thing) {
structB temp = {0};
temp.b = thing.b;
temp.c = thing.c;
temp.d = thing.d;
return funcThing (temp);
}
Альтернативой вышеперечисленному может быть что-то вроде:
int funcThing1 (structB thing);
int funcThing2 (structB thing);
int funcThing3 (structB thing);
int funcThingSet_structA (structA thing, int (*f)(structB thing)) {
structB temp = {0};
temp.b = thing.b;
temp.c = thing.c;
temp.d = thing.d;
return f (temp);
}
// and the above is used like
structA thingA;
// … code
i = funcThingSet_structA (thingA, funcThing1); // call funcThing1() with the structA data
i = funcThingSet_structA (thingA, funcThing2); // call funcThing2() with the structA data
i = funcThingSet_structA (thingA, funcThing3); // call funcThing3() with the structA data
Если функции могут изменять данные, то вам нужно убедиться, что structA
обновляется как в:
int funcThing1 (structB *thing);
int funcThing2 (structB *thing);
int funcThing3 (structB *thing);
int funcThingSet_structA (structA *thing, int (*f)(structB *thing)) {
structB temp = {0};
int iRetVal = 0;
temp.b = thing->b;
temp.c = thing->c;
temp.d = thing->d;
iRetVal = f (&temp);
thing->b = temp.b;
thing->c = temp.c;
thing->d = temp.d;
return iRetVal;
}
// and the above is used like
structA thingA;
// … code
i = funcThingSet_structA (&thingA, funcThing1); // call funcThing1() with the structA data
i = funcThingSet_structA (&thingA, funcThing2); // call funcThing2() with the structA data
i = funcThingSet_structA (&thingA, funcThing3); // call funcThing3() with the structA data
Вы также можете иметь API в терминах structB
и использовать вспомогательные функции интерфейса, такие как:
structB *AssignAtoB (structB *pB, structA A) {
pB->b = A.b;
pB->c = A.c;
pB->d = A.d;
return pB;
}
structB ConvertAtoB (structA A) {
structB B = {0};
B.b = A.b;
B.c = A.c;
B.d = A.d;
return B;
}
void AssignBtoA (structA *pA, structB B) {
pA->b = B.b;
pA->c = B.c;
pA->d = B.d;
}
Тогда вы можете сделать что-то вроде:
int funcThing1 (structB thing);
int funcThing2 (structB thing);
int funcThing3 (structB thing);
structA aThing;
// …. code
{ // create a local scope for this temporary bThing.
structB bThing = ConvertAtoB (aThing);
i = funcThing1(bThing);
// other operations on bThing and then finally.
AssignBtoA (&aThing, bThing);
}
Или ваши функции API могут возвращать structB
, в этом случае вы можете сделать что-то вроде:
structB funcThing1 (structB thing);
structB funcThing2 (structB thing);
structB funcThing3 (structB thing);
structA aThing;
// …. code
{ // create a local scope for this temporary bThing.
structB bThing = ConvertAtoB (aThing);
bThing = funcThing1(bThing);
bThing = funcThing2(bThing);
AssignBtoA (&aThing, bThing);
}
или
{ // create a local scope for this temporary bThing.
structB bThing = ConvertAtoB (aThing);
AssignBtoA (&aThing, funcThing2(funcThing1(bThing)));
}
или даже просто
AssignBtoA (&aThing, funcThing2(funcThing1(ConvertAtoB (aThing))))
Альтернатива 3 - хрупкое использование указателей
Другой альтернативой является создание указателя, адрес которого начинается с части structB
в [ 1172]. Хотя я только смутно знаком с MISRA, я почти не сомневаюсь, что этот подход противоречит правилам, поскольку он в значительной степени мерзок. Тем не менее, здесь все равно, как я видел, это было сделано в старом коде, написанном людьми без надлежащей подготовки специалистов по разработке программного обеспечения.
Используя две вышеупомянутые структуры, создайте вспомогательную функцию или макрос, который сгенерирует указатель на смещение в structA
, где начинаются данные structB
. Например:
structB MakeClone (structA thing) {
return *(structB *)&thing.b; // return a copy of the structB part of structA
}
или
structB *MakePointer (structA *pThing) {
return (structB *)&thing.b; // return a pointer to the structB part of structA
}
Макрос препроцессора также может использоваться для генерации указателя второго случая, как в:
#define MAKEPOINTER(pThing) ((structB *)&((pThing)->b))
I также видели, где вместо использования вспомогательной функции с присваиванием, как в:
int funcThing (structB *pBthing);
// then in code want to use the above function with a structA
structA aThing = {0};
// do things with aThing then call our function that wants a structB
funcThing (MAKEPOINTER(&aThing));
вместо этого они просто жестко закодируют указатель, в результате чего действительно трудно найти, где это было сделано во время обслуживания: [ 11115]
funcThing ((structB *)&(aThing.b));
Я также видел подход указателя, используемый с присваиванием, используя memcpy()
для выполнения присваивания. Поэтому, если у нас есть код, подобный следующему:
structA aThing = {0};
structB bThing = {0};
// somewhere in code we have
memcpy (&bThing, &aThing.b, sizeof(structB)); // assign the structB part of aThing to a structB
// more code to modify bThing then call our function
funcThing (&bThing);
memcpy (&aThing.b, &bThing, sizeof(structB)); // assign the structB back into the structB part of aThing
Использование подхода с указателем хрупко, потому что, если компоновка structA
или компоновка structB
должна измениться, вещи, вероятно, сломаются. Хуже всего то, что они могут сломаться без указания причины и основной причины.
Вы можете создать интерфейс. Например,
struct A {}; // As you have defined
void set_b_for_A(struct A, int) {} // function that works with A
// interface
struct helper {
void *ptr; // pointer to your structs variants (A, ...)
void (*set_b)(struct helper *, int); // helper to dispatch to correct worker
};
void set_b_helper_for_A(struct helper *pointer, int i) { // helper for worker A
struct A *s = (struct A *) pointer->ptr;
set_b_for_A(*s, i);
}
struct helper helper_A {/* A struct */, set_b_helper_for_A};
Теперь ваш API
void set_b(struct helper *ptr, int i) {
ptr->set_b(ptr, i);
}
и вы звоните, например:
set_b(&helper_A, 0);
Сделайте то же самое для других структур