2016-08-21 10 views
0

Скажем, я разрабатываю математическую библиотеку, и я хочу, чтобы она была такой, чтобы она определяла, поддерживает ли машина пользователя SSE (и какая версия), и на основе этого отдельные внутренние функции будут вызывается для той же функции API. Я могу думать о трех способах реализации, что:C99: Динамическая отправка в статической библиотеке

  1. Есть глобальных указателей на функцию в библиотеке и пусть mathInit вызова пользователя() в их источнике. Когда они это сделают, выясните детали оборудования и назначьте указатели функций на разные функции.

  2. То же, за исключением вместо глобальных указателей функций, поместите их в struct, который возвращается mathInit(). Таким образом, пользователь должен будет вызвать math.vec3Add (...) или аналогичный.

  3. То же, что 1, но вместо того, чтобы глобальные указатели, сделать mathInit() а макрос так, что указатели на функции будут иметь локальную область видимости в основной функции пользователя() (и требуют mathInit() будет вызываться из основных()). Конечно, это будет в заголовке.

Есть ли какой-либо из этих методов? Есть ли другой, лучший способ?

+0

3 просто неприемлемо, нет ifs или buts здесь. –

+0

@ н.м. Я раньше не работал с указателями функций, поэтому все трое казались довольно неэлегантными. Следовательно, вопрос. –

ответ

1

Это в значительной степени основано на мнениях, ИМХО.

И, по моему мнению, математическая библиотека должна раскрывать наименьшую возможную информацию о ее внутренней работе и не должна требовать сложных указателей функций или структур данных или даже макросов для работы с кодом пользователя, если это возможно.

Я бы пошел с (1) и предположил, что вы полностью спрячете указатели на функции в своей библиотеке, то есть вызовите их через косвенное в библиотечном коде.

(3) определенно самый худший вариант, поскольку он накладывает ограничения на код пользователя, который явно не очевиден. Это может также создать неочевидные проблемы/наблюдения при отладке кода пользователя.

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

Вы также можете открыть функцию hasSSE вместе с функциями SSE и не SSE и оставить решение, что использовать для кода пользователя. Не уверен, что это принесет пользу (1).

+0

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

+0

Я не вижу никаких технических проблем с переменными в общей библиотеке. В любом случае им не нужно будет * global * для приложения. Вы можете легко сделать их «статическими» для их косвенности в библиотеке. И «нетрадиционный интерфейс» - помните, что особенно математические функции часто используются непрофессиональными программистами. Я бы избегал этого любой ценой. – tofro

+0

В основном библиотека будет использоваться внутри моего собственного движка. И в отношении глобальных переменных в общих библиотеках: http://nullprogram.com/blog/2014/12/23/ [третий абзац] –

0

Мое предложение будет составлять отдельный блок для каждого набора команд (например, Xnosse.o Xsse3.o Xsse4.o и т. Д.) И использовать для этого автоматический диспетчер. Пользователь должен получить максимальную производительность для своего ПК и не заботиться о внутренней детализации.

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

Вот пример кода (только gcc!)

единиц компиляции:

//Xnosse.c 
void do_some_math_stuff_no_sse(int x, int y) 
{ 
    ...do some sophisticated math stuff with no sse support 
} 
void do_some_other_math_stuff_no_sse(int x, int y) 
{ 
    ...do some other sophisticated math stuff with no sse support 
} 

//Xsse3.c 
void do_some_math_stuff_sse3(int x, int y) 
{ 
    ...do some sophisticated math stuff with sse3 support 
} 
void do_some_other_math_stuff_sse3(int x, int y) 
{ 
    ...do some other sophisticated math stuff with sse3 support 
} 

//Xsse4.c 
void do_some_math_stuff_sse4(int x, int y) 
{ 
    ...do some sophisticated math stuff with sse4 support 
} 
void do_some_other_math_stuff_sse4(int x, int y) 
{ 
    ...do some other sophisticated math stuff with sse4 support 
} 

Сейчас в библиотеке:

//my_math.h 
/* Following definitions are in my_math.c */ 
extern void (*do_some_math_stuff)(int x, int, y); 
extern void (*do_some_other_math_stuff)(int x, int y); 

//my_math.c 
void not_set(int x, int y) 
{ 
    // If you don't want to use the constructor for any reason, 
    // say you want lazy binding, this will do the trick as our 
    // functions do_math_stuff and do_other_math_stuff are initialized 
    // to this one 
    setup(); 
} 

void (*do_some_math_stuff)(int x, int, y) = not_set; 
void (*do_some_other_math_stuff)(int x, int y) = not_set; 

int detect_sse() 
{ 
    ..Do runtime detection of sse version 
} 

/* The following function will be called when your library loads */ 
void __attribute__ ((constructor)) setup(void) 
{ 
    if (detect_sse() == 0) 
    { 
     do_some_math_stuff = do_some_math_stuff_no_sse; 
     do_some_other_math_stuff = do_some_other_math_stuff_no_sse; 
    } 
else if (detect_sse() == 3) 
    { 
     do_some_math_stuff = do_some_math_stuff_sse3; 
     do_some_other_math_stuff = do_some_other_math_stuff_sse3; 
    } 
else if (detect_sse() == 4) 
    { 
     do_some_math_stuff = do_some_math_stuff_sse4; 
     do_some_other_math_stuff = do_some_other_math_stuff_sse4; 
    } 
} 

Если вы хотите ленивым связывание, удалите конструктор decorater из установки и компиляции с:

gcc -Wall -shared -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c 

Если вы хотите, чтобы динамический диспетчер работал, когда загрузка библиотеки использует следующие дополнительные параметры для g cc:

gcc -Wall -shared -Wl,-init,setup -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c 
+0

Это в основном использование глобальных символов, только пользователю не нужно вызывать mathInit() , у меня нет проблемы с mathInit, у механизма будет вся подсистема инициализации. Вопрос в том, правильно ли используется глобальный указатель на функции? –

+0

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