Я знаю, что гибкий член массива не является частью стандарта C ++ 11.
Так что же правильный способ взаимодействия с кодом C, который возвращает или принимает в качестве аргумента, структуры с гибким членом массива, из C ++ 11?
Должен ли я написать шим, который отображает член гибкого массива из структуры C на указатель в C ++?
Насколько мне известно, стандартный C ++ даже не примет объявление структуры с гибким членом массива. В таком случае я не вижу альтернативы, кроме как писать функции-оболочки (на C), если только тип структуры, содержащий FAM, не может быть непрозрачным для вашего кода C ++. Я не уверен, является ли обертка той подкладкой, которую вы имели в виду.
Однако, прежде чем мы пойдем дальше, я должен отметить, что проблема существенно отличается, если ваши функции C принимают и возвращают указатели на структуры с гибким членом массива, чем если бы они проходили и возвращали фактические структуры. Я предполагаю, что они работают с указателями на эти структуры, так как в противном случае, кажется, нет смысла иметь FAM в первую очередь.
Я думаю, что с учетом объявления C, такого как
struct foo {
int count;
my_type fam[];
};
Я бы представлял те же данные в C ++, что и
struct cpp_foo {
int count;
my_type *fam;
};
, что, конечно, может быть обработано и C. Имейте в виду, что вы не можете успешно привести между ними, потому что массивы не являются указателями.
Учитывая функцию C
struct foo *do_something(struct foo *input);
тогда необходимая оболочка может выглядеть так:
struct cpp_foo *do_something_wrap(struct cpp_foo *input) {
struct cpp_foo *cpp_output = NULL;
// Prepare the input structure
size_t fam_size = input->count * sizeof(*input->fam);
struct foo *temp = malloc(sizeof(*temp) + fam_size);
if (!temp) {
// handle allocation error ...
} else {
struct foo *output;
temp->count = input->count;
memcpy(temp->fam, input->fam, fam_size);
// call the function
output = do_something(temp);
if (output) {
// Create a copy of the output in C++ flavor
cpp_output = malloc(sizeof(*cpp_output));
if (!cpp_output) {
// handle allocation error
} else {
fam_size = output->count * sizeof(output->fam[0])
cpp_output->fam = malloc(fam_size);
if (!cpp_output) // handle allocation error
memcpy(cpp_output->fam, output->fam, fam_size);
// Supposing that we are responsible for the output object ...
free(output);
}
} // else cpp_output is already NULL
free(temp);
}
return cpp_output;
}
Естественно, если у вас есть несколько функций для переноса, вы, вероятно, захотите написать многократно используемые функции преобразования, чтобы упростить их.
Поскольку члены гибкого массива не могут подвергаться воздействию C ++ (my_type fam[];
не является допустимым членом C ++), нам нужно определить ваш собственный тип.
К счастью, у функций связи C нет символов, которые зависят от их аргументов. Таким образом, мы можем либо изменить определение foo
в общих заголовках, или определить наши собственные и не включать их заголовки.
Это struct
это, вероятно, будет совместимо с макетом. Обратите внимание, что вы никогда не должны объявлять их в стеке в C ++:
struct foo {
int count;
#ifndef __cplusplus
my_type fam[];
#else
my_type do_not_use_fam_placeholder;
my_type* fam() {
return &do_not_use_fam_placeholder;
}
my_type const* fam() const {
return &do_not_use_fam_placeholder;
}
#endif
};
Это зависит от двоичного макета foo
структура в C должна быть элементами префикса, за которыми следуют элементы элемента гибкого массива, без дополнительной упаковки или выравнивания. Это также требует, чтобы член гибкого массива никогда не был пустым.
я хотел бы использовать this+1
но это сталкивается с проблемами выравнивания, если есть заполнение между count
а также fam
,
Использование memcpy
или же memmov
или тому подобное на foo
не рекомендуется. В общем, создание foo
на стороне C ++ не очень хорошая идея. Если вам нужно, вы можете сделать что-то вроде этого:
struct foo_header {
int count;
};
foo* create_foo_in_cpp(int count) {
std::size_t bytes = sizeof(foo)+sizeof(my_type)*(count-1);
foo* r = (foo*)malloc(bytes);
::new((void*)r) foo_header{count};
for (int i = 0; i < count; ++i)
::new( (void*)(r->fam()+i) my_type();
return r;
};
который создает каждый объект в C ++. Правила существования объектов в C ++ более строгие, чем в C; просто взять некоторую память POD и интерпретировать ее как POD не является допустимым действием в C ++, в то время как в C. new
s выше будет оптимизирован для noops во время выполнения, но C ++ требует объявить, что рассматриваемая память должна рассматриваться как объекты этого типа при строгом чтении стандарта.
Теперь есть некоторые стандартные проблемы (дефекты) с ручным построением элементов массива для каждого элемента и совместимостью компоновки между массивами и элементами, так что вам придется несколько поверить, что ABI компилятора C ++ и кода C совместимо (или проверьте это).
В общем, все взаимодействие между C и C ++ не определено стандартом C ++ (за исключением некоторых частей стандартной библиотеки C, которые включает в себя C ++; даже в этом случае нет никакого мандата на использование C ++ той же библиотеки C). Вы должны понимать, как ваша конкретная реализация C и C ++ взаимодействует.