#c #c #casting #mixing
#c #c #Кастинг #смешивание
Вопрос:
Мне нужно смешивать код C с кодом C в компьютерном моделировании.
Библиотека C имеет функции, которые принимают ссылки на обычный массив двойников:
void position(double[3])
Другая библиотека C определяет свой собственный vector
тип:
void do_something(*custom_vector)
И мой интерфейс (на C ) использует boost::numeric::ublas::vector
.
В первом случае у меня есть много мест с таким кодом:
double tmp[3];
position(tmp)
boostvec r(3);
r(0) = tmp[0]; r(1) = tmp[1]; r(2) = tmp[2];
// continue working with r
также
custom_vector *v;
do_something(v);
boostvec r(3);
r(0) = v[0]; r(1) = v[1]; r(2) = v[2];
Внутренне все типы в конечном счете являются векторными контейнерами, но их небольшие различия в реализации вызывают много шаблонного взрыва. Я также имею дело с множеством разных версий double
(некоторые определяют realtype
(что является двойным), другие определяют number
(что является двойным) И т. Д.).
Как вы справляетесь с этой ситуацией?
Спасибо.
Комментарии:
1. Предоставить автономные функции преобразования?
2. @ThomasMatthews: Я думал об этом, но я все же хотел бы изучить возможность передачи базового массива boost функциям C (возможно, это ужасная идея, но я подумал, что должен спросить;)
Ответ №1:
До тех пор, пока оба типа источника и назначения поддерживают стандартный интерфейс итератора (что верно для std::vector, массивов и каждого типа последовательности повышения, который я могу придумать; поэтому вам просто нужно исправить свой custom_vector), и до тех пор, пока возможны неявные преобразования между числовыми типами, выдолжна быть возможность просто использовать std::copy
.
Комментарии:
1. Я протестировал это, и это работает. Теперь, есть ли способ избежать временного хранилища? Другими словами, могу ли я передать базовый массив boost функциям C?
2. @Arrieta: если базовое представление похоже на массив (а это почти всегда так),
amp;a[0]
должно работать.
Ответ №2:
Если вы переводите между двумя типами данных подобным образом, вам, вероятно, следует писать много котельной плиты. Это всегда будет одна и та же операция, независимо от того, сколько помады вы на нее нанесете.
Когда вы видите typedefs для таких вещей, как realtype
, для меня это запах кода, что кто-то слишком рано абстрагирует данные. Написание абстракций вокруг данных на более низких уровнях, как правило, приводит к тому, что интерфейсы «взрываются» вот так. Если вы используете контейнеры данных самым глупым образом, вам, как правило, лучше. Не защищайте ими инварианты, просто храните данные. Создавайте абстракции на уровне алгоритма и оставляйте данные в их простейшей форме.
Возможно, имеет смысл провести рефакторинг другого кода, чтобы все они использовали что-то согласованное, но обычно это старый или внешний код. В вашей новой разработке будьте в здравом уме и используйте векторы. Напишите адаптеры для перевода между ними и просто позвольте старому коду продолжать высасывать ваш внешний вид.
Самое счастливое, что я был в том, чтобы избежать ужасно запутанных проблем такого рода, — это использовать указатели и длину. По общему признанию, это противоречит большинству советов.
void foo(double *data, size_t len);
Это приводит к уродливому (и подверженному ошибкам) коду, но это наименьший общий знаменатель. Очевидно, что если вы можете скрыть это в частной реализации, это намного лучше.
В итоге я написал множество помощников, которые невозможно было использовать повторно. В результате код имел ужасную локальность, и мои намерения не были очевидны.
Комментарии:
1. отличный пост. Мне особенно нравится «пусть старый код продолжает высасывать ваш внешний вид» — я буду стремиться к этому.