2013-06-18 2 views
1

Я реализую небольшую программу на C, которая использует общую библиотеку под названием libhelper.so. «libhelper.so» определяет структуры в это час-файл, но, к сожалению, в зависимости от целевой системы этих определений различны (libhelper.so всегда обеспечивается системой, а не сам):1 struct с двумя различными определениями (или динамическими структурами)

системы A:

struct theStruct { 
     int fd; 
     unsigned int flags; 
     struct config config; // only in System A 
     int foo; // in both systems 
     int bar; // only in System A 
}; 

система B:

struct theStruct { 
     int fd; 
     unsigned int flags; 
     int foo; // in both systems 
     int foobar; // only in system B 
}; 

В моей программе, я думал, что я просто определить, что сам-структуру, как это:

struct theStruct { 
     int fd; 
     unsigned int flags; 
     struct config config; // only in System A 
     int foo; // in both systems 
     int foobar; // only in system B 
     int bar; // only in System A 
}; 

В результате вызова функции в «libhelper.so» Я получил экземпляр «theStruct», и теперь я, хотя я могу просто проверить, если «theStructInstance-> бар» или «theStructInstance-> foobar "заполняется действительным значением, чтобы определить, какая реализация была использована библиотекой.

Но кажется, что я получаю только значения, подобные 1 ... 6, которые выглядят как позиция поля в структуре.

Есть ли у кого-нибудь идеи, как я могу это сделать?

+0

You не упоминайте, как назначается целевая система и как используются функции в libhelper.so. Вопрос также в том, существуют ли другие структуры и определяет и другие вещи, которые зависят от целевой системы. Не могли бы вы предоставить дополнительную информацию об этом? –

ответ

0

Причина, почему ваше предложение выиграло» t заключается в том, что смещение к члену foo отличается для системы A и системы B. Вы говорите, что можете узнать только, какую систему вы используете во время выполнения. Итак, когда System B устанавливает foo, это, скорее всего, в конечном итоге установит что-то внутри config.

enum system { SystemUnknown, SystemA, SystemB }; 

struct theStructSystemA { 
     int fd; 
     unsigned int flags; 
     struct config config; // only in System A 
     int foo; // in both systems 
     int bar; // only in System A 
}; 

struct theStructSystemB { 
     int fd; 
     unsigned int flags; 
     int foo; 
     int foobar; 
}; 

struct myStruct { 
     union { 
      struct theStructSystemA a; 
      struct theStructSystemB b; 
     } u; 
     enum system sys; 
}; 

struct myStruct s = { 0 }; 

Теперь вы можете установить bar в какой-то недопустимое значение: s.u.a.bar = -1, например. Теперь, когда вы звоните в библиотеку, вы можете проверить:

s.u.a.bar = -1; 
some_libhelper_call((void *)&s); 
if (s.u.a.bar != -1) s.sys = SystemA; 
else s.sys = SystemB; 

Так что теперь, после того, как s.sys известно, вы можете переключиться на другой код пути, который имеет дело исключительно с известной версией системы.

+0

Большое спасибо за ваш ответ. Я постараюсь принять ваше решение, потому что оно почти точно соответствует моим требованиям и кажется очень * умным! Но я дам также +1 для каждого другого ответа, потому что все они правильные, хорошие и есть решение для каждой ситуации. Спасибо вам всем! –

+0

Добро пожаловать. – jxh

5

Нет, это не сработает.

Прежде всего, все определения структуры должны быть одинаковыми, иначе вы получите печально известное Undefined Behavior.

Во-вторых, посмотрите на макет памяти. Каким должно быть смещение до bar с начала структуры? Первое и третье определения не согласуются с этим (поля, скорее всего, будут расположены последовательно).

Возможно, вы можете попробовать союз?

struct theStruct { 
     int fd; 
     unsigned int flags; 
     struct config config; 
     int foo; // in both systems 
     union { 
      int bar; // only in System A 
      int foobar; // only in system B 
     }; 
}; 

Если выбрать этот вариант, вы должны использовать только bar на системе и только foobar на системе В.


Если две системы несовместимы, а фактический тип необходим для bar не доступно на системе B (и наоборот), вы можете сделать с помощью следующего кода:

struct theStruct { 
     int fd; 
     unsigned int flags; 
     struct config config; 
     int foo; // in both systems 
#ifdef SYSTEM_A 
     int bar; // only in System A 
#else 
#ifdef SYSTEM_B 
     int foobar; // only in system B 
#else 
#pragma error(either SYSTEM_A or SYSTEM_B must be enabled) 
#endif 
#endif 
}; 

Таким образом, вы всегда будете работать с еи ther с кодом, скомпилированным для System A или для System B, поэтому вам понадобятся разные исполняемые файлы (что, похоже, неизбежно, если вы компилируете для разных систем).

Вам нужно обернуть части вашего кода доступа к полям в #ifdef с:

#ifdef SYSTEM_A 
s.bar = 5; 
#endif 

- в противном случае вы получите ошибки компиляции на системе B.

+0

Это конкретное неопределенное поведение настолько четко определено, что почти каждая современная операционная система критически зависит от него. – Wug

+0

@Wug: писать переносимый код сложно даже без UB, поэтому я не буду полагаться на переносимость UB в любом случае. – Vlad

+0

Большое спасибо. Я уже думал, что это будет проблемой, но я относительно новичок в C и все еще изучаю.Союз для меня совершенно новый, должен прочитать документы об этом и продолжать искать способ обойти всю ситуацию :) –

2

Другим возможным решением было бы написать код, зависящий от платформы, для обработки каждой из отдельных структур, а затем загрузить их данные в общую структуру.Это затем позволит вам иметь дело с теми же членами структуры, независимо от пути кода, а не всегда ссылок на один из двух членов профсоюза на основе системы:

struct mystruct; 
mystruct.member1 = theStruct.member1; //the common part of the struct 
mystruct.member2 = theStruct.member2; 
#ifdef platform1 
    mystruct.member3 = theStruct.p1member; //specific to platform1 
    mystruct.member4 = -1; 
#else 
    mystruct.member3 = -1; 
    mystruct.member4 = theStruct.p2member; //specific to platform2 
#endif 
+0

Спасибо за ваш ответ. Это было мое первое предложение, но, к сожалению, у меня нет информации о целевой системе во время компиляции, поэтому ifdef здесь проблематично :) –

+0

@MartinM .: Почему вы не знаете целевую систему во время компиляции? Вы используете один и тот же двоичный файл на нескольких целевых системах? – jxh

+0

@Martin: У вас должна быть эта информация. Когда вы компилируете для платформы # 1, создайте 'platform1' (для этого должен быть флаг компилятора), то же самое о платформе 2. Сделайте код не компилируемым, если не определены ни« платформа1 »,« платформа2 » , – Vlad

1

Вот приблизительный подход.

Немного о предпосылках, которые я делаю первым.

Похоже, у вас есть функция libraryFunction(), которая возвращает указатель на struct theStruct. Однако фактическое расположение struct theStruct зависит от конкретной системы, на которой работает ваше приложение. В этой структуре есть некоторая информация, которую вам нужно получить. Вы не указываете вызывающие аргументы или подпись функции библиотеки, и если указатель на struct theStruct возвращается как значение функции или указатель на указатель является частью списка аргументов. Я предполагаю, что это возвращаемое значение функции.

Создайте структуру, которую вы определяете для информации, которую вы хотите. Создайте два файла, каждый из которых имеет функцию, которая принимает указатель void и указатель на вашу новую структуру, а затем заполняет вашу структуру данными из библиотеки, предоставленной структурой. Каждый из этих двух файлов будет скомпилирован с определенной целевой системой (SystemA или SystemB), чтобы ваша функция преобразования интерпретировала структуру, предоставляемую библиотечной функцией, в зависимости от целевой системы и заполнила вашу структуру необходимыми данными.

файл 1 для системы

// copy of the struct used in System A which is in the library header file 
// put here for reference only as should be in the header file 
struct theStruct { 
    int fd; 
    unsigned int flags; 
    struct config config; // only in System A 
    int foo; // in both systems 
    int bar; // only in System A 
}; 

// my struct that contains the data from struct theStruct that I want 
// would be in a header file included into each of these files but here for reference 
struct myConvertStruct { 
    int foo; 
}; 

void convert2SystemA (void *structPtr, struct *myStruct) 
{ 
    myStruct->foo = ((struct theStruct *)structPtr)->foo; 
} 

файл 2 для System B

// copy of the struct used in System B which is in the library header file 
// put here for reference only as should be in the header file 
struct theStruct { 
    int fd; 
    unsigned int flags; 
    int foo; // in both systems 
    int foobar; // only in system B 
}; 

// my struct that contains the data from struct theStruct that I want 
// would be in a header file included into each of these files but here for reference 
struct myConvertStruct { 
    int foo; 
}; 

void convert2SystemB (void *structPtr, struct *myStruct) 
{ 
    myStruct->foo = ((struct theStruct *)structPtr)->foo; 
} 

файл 3 с помощью функции преобразования

// my struct that contains the data from struct theStruct that I want 
// would be in a header file included into each of these files but here for reference 
struct myConvertStruct { 
    int foo; 
}; 

{ 
    struct myConvertStruct myStruct; 
    // some function body and now we come to the library call 

    if (mySystem == SystemA) { 
    void *pStruct = libraryFunction (......); 
    convert2SystemA (pStruct, &myStruct); 
    } else if (mySystem == SystemB) { 
    void *pStruct = libraryFunction (......); 
    convert2SystemB (pStruct, &myStruct); 
    } else { 
    // some error conditions 
    } 
    // now use the data that you have pulled as you want to use it 

} 
Смежные вопросы