Работа с разными типами с одной и той же базовой структурой данных

#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. отличный пост. Мне особенно нравится «пусть старый код продолжает высасывать ваш внешний вид» — я буду стремиться к этому.