Вопрос взаимодействия с C#.
у меня есть библиотека С++, которая использует структуру:
struct CPPSTRUCT
{
long v1;
short v2;
long v3;
}
В C# у меня есть соответствующая структура:
[StructLayout(LayoutKind.Sequential)]
public struct MANAGEDSTRUCT
{
public int v1;
public short v2;
public int v3;
}
Он работает, пока библиотека C++ создана для Windows x64, однако не работает, когда та же библиотека создается для Linux x64. Причина его поломки заключается в том, что длина long
в C++ составляет 4 байта в Win x64, но 8 байт в Linux x64: изменение типов int
в типах MANAGEDSTRUCT
на long
устраняет проблему для Linux x64, но ломает ее для Windows x64.
Другая проблема заключается в том, что установка/получение значений v1
и v3
в MANAGEDSTRUCT
также может стать условным.
Я ищу лучший подходящий для C# способ условного объявления типов переменных/маршаллов в зависимости от ОС/архитектуры, чтобы приложение, использующее библиотеку C++, могло ориентироваться на несколько платформ, не беспокоясь о типах, и иметь возможность устанавливать значения в MANAGEDSSTRUCT
с помощью клиент.
Спасибо!
В дополнение к тому, что указал @JeremyLakeman, это ужасно определенная структура C. В зависимости от компилятора, архитектуры машины, а также настроек упаковки и выравнивания расположение этой структуры в памяти может измениться (даже размеры элементов могут измениться (long
могут быть 4 или 8 байтов). Вы уверены, что это то, что ваш C ( /C++) выглядит так. Когда я был разработчиком C (а позже и C++), я быстро научился настраивать определения структур с учетом упаковки и выравнивания.
Спасибо: изменение приложения на C++ не будет простым, но не невозможным, и хотя это, очевидно, упрощение для целей иллюстрации, структура C++, с которой я имею дело, выглядит примерно так. Любопытно: как должно измениться определение структуры C++, чтобы оно стало более независимым/педантичным в отношении типов платформы?
@concentriq При использовании типов в <cstdint>, v1
и v3
будет использоваться int32_t
, а v2
будет использоваться int16_t
. Таким образом, v1 и v3 всегда будут 32-битными (фиксированный размер int в C#), v2 всегда будут 16-битными (фиксированный размер short в C#).
Ах, но это будет по-другому на машинах (или настройках компилятора), требующих 32- или 64-битного выравнивания, по сравнению с традиционными слабыми настройками x86.
А как насчет создания разных конфигураций для разных платформ и определения соответствующих констант, а затем использования условных директив в вашем коде и абстрагирования деталей взаимодействия?
Вот так (грубый пример)
#if LINUX64
[StructLayout(LayoutKind.Sequential)]
public struct MANAGEDSTRUCT
{
public long v1;
public short v2;
public long v3;
}
#else
[StructLayout(LayoutKind.Sequential)]
public struct MANAGEDSTRUCT
{
public int v1;
public short v2;
public int v3;
}
#endif
public int DoSomething(int v1, short v2, int v3)
{
MANAGEDSTRUCT s = new MANAGEDSTRUCT();
s.v1 = v1;
s.v2 = v2;
s.v3 = v3;
// Do interop stuff
return (int)s.v1; // I assume that the value will fit in 4 bytes since it work under Windows
}
P.S.
Библиотека C++ разработана вами/вашими коллегами? Если да, я бы предложил переписать его, чтобы указать размер полей структуры независимым от платформы способом (int32_t или int64_t).
Не уверен, что это сработает, но вы можете попробовать псевдоним глобальных типов следующим образом (псевдокод)
#if Windows
global using NativeLong = System.Int32; //int
#elseif Linux
global using NativeLong = System.Int64; //long
#endif
[StructLayout(LayoutKind.Sequential)]
public struct MANAGEDSTRUCT
{
public NativeLong v1;
public short v2;
public NativeLong v3;
}
Тогда вы сможете использовать это NativeLong
во всей кодовой базе.
Спецификации псевдонима глобального типа приведены здесь
P.S. Пожалуйста, просмотрите комментарии под вопросом (особенно @Flydog57 и @ipodtouch0218 - похоже, там показан лучший подход)
Проблема, скорее всего, в выравнивании, а не в размере шрифта. Если это внешний API, я бы изменил код C++ (если можете), чтобы он был педантичным в отношении размеров типов и выравнивания памяти.