Как правильно взаимодействовать с членами гибкого массива C из C ++?

Я знаю, что гибкий член массива не является частью стандарта C ++ 11.

Так что же правильный способ взаимодействия с кодом C, который возвращает или принимает в качестве аргумента, структуры с гибким членом массива, из C ++ 11?

Должен ли я написать шим, который отображает член гибкого массива из структуры C на указатель в C ++?

1

Решение

Насколько мне известно, стандартный 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;
}

Естественно, если у вас есть несколько функций для переноса, вы, вероятно, захотите написать многократно используемые функции преобразования, чтобы упростить их.

3

Другие решения

Поскольку члены гибкого массива не могут подвергаться воздействию 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. news выше будет оптимизирован для noops во время выполнения, но C ++ требует объявить, что рассматриваемая память должна рассматриваться как объекты этого типа при строгом чтении стандарта.

Теперь есть некоторые стандартные проблемы (дефекты) с ручным построением элементов массива для каждого элемента и совместимостью компоновки между массивами и элементами, так что вам придется несколько поверить, что ABI компилятора C ++ и кода C совместимо (или проверьте это).

В общем, все взаимодействие между C и C ++ не определено стандартом C ++ (за исключением некоторых частей стандартной библиотеки C, которые включает в себя C ++; даже в этом случае нет никакого мандата на использование C ++ той же библиотеки C). Вы должны понимать, как ваша конкретная реализация C и C ++ взаимодействует.

0

По вопросам рекламы [email protected]