Использование #define для настройки библиотеки - PullRequest
4 голосов
/ 30 января 2011

Я работаю над математической библиотекой C ++, в которой я хочу настроить во время компиляции, используя define.

Одна из конфигураций определяет точность.В коде это выглядит так:

#ifdef MYMATH_USE_DOUBLE
    typedef double Real;
#else
    typedef float Real;
#endif

Это прекрасно работает.

Если кто-то захочет использовать библиотеку после того, как она настроена на MYMATH_USE_DOUBLE, ему придется также передатькоторые определяют для компилятора.

Есть ли лучший способ сделать это?

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

Ответы [ 5 ]

2 голосов
/ 30 января 2011

Я бы предложил использовать шаблоны с двойным значением по умолчанию.

template <typename F = double>
F sin(const F& r)
{
//...
}

Таким образом, пользователи могут использовать функции как есть для двойников, но у них есть возможность изменить тип:

float f = sin<float>(r);

РЕДАКТИРОВАТЬ: система шаблонов должна автоматически сделать вывод, что F в этом случае является float, учитывая, что r является float.

2 голосов
/ 30 января 2011

Предоставьте два параллельных набора функций, один для реализации с использованием float, а другой для реализации с использованием double (и третий для long double). Это то, что делает библиотека C - есть sin() для double, sinf() для float и sinl() для long double.

Или в C ++ вы могли бы (вероятно, должны?) Рассмотреть возможность использования перегрузок или шаблонов. Однако я подозреваю, что это Это может привести к путанице, а не к простоте (или, преимущественно, он будет использовать перегрузки double, поскольку литералы с плавающей запятой равны double, если не указаны суффиксы явно), но шаблоны часто являются методом выбора в наши дни.

( Комментарии изменены в свете комментариев bstamour ; я был слишком консервативен и в 1990-х годах. )

1 голос
/ 30 января 2011

Как уже предлагали другие, использование шаблонов является возможным решением вашей проблемы. Однако, если вы предоставите свой общий код пользователям, им придется перекомпилировать вашу библиотеку при выборе другого типа. Возможно, имеет больше смысла использовать только общий код для внутреннего использования и предоставлять вашему интерфейсу фиксированный набор типов, используя обычную перегрузку функций:

// generic implementation (internal linkage):
namespace {
    template<typename Real>
    Real plus42(Real value) {
        return value + 42;
    }
}

// API functions (external linkage):
float plus42(float value) { return plus42<>(value); }
double plus42(double value) { return plus42<>(value); }

Принимая во внимание цепочку инструментов GNU, вы должны быть в состоянии избежать добавления мертвого кода при статической компоновке, передав -fvtable-gc -ffunction-sections -fdata-sections компилятору и -Wl,--gc-sections компоновщику.

1 голос
/ 30 января 2011

Обычно рекомендуется запускать скрипт «configure», который создает один файл со всеми определениями.И этот файл включен во все заголовки.Например, если вы компилируете OpenSSL из источников, «configure» создает e_os.h (насколько запоминается имя), которое включено практически в каждый заголовок.

0 голосов
/ 30 января 2011

Поместите условное определение в заголовочные файлы вашего lin (если его там еще нет) и поместите директиву поиска компилятора в соответствующий lib-файл (если он включен клиентами).

#ifdef MYMATH_USE_DOUBLE
    typedef double Real;  
$ifndef _LIB // only for clients
#pragma comment( lib, "double_lib" ) // double_lib name of the library. 
#endif
#else 
    typedef float Real;  
$ifndef _LIB
#pragma comment( lib, "float_lib" ) 
#endif
#endif  
...