Каковы практические применения слабых связей?
Используя специальные команды компилятора, символ может быть объявлен слабым. Согласно Википедии:
слабый символ - это определение символа в объектном файле или динамической библиотеке, которая может быть переопределена другими определениями символов
В каких сценариях или для каких приложений вам нужны слабые символы? Каковы типичные варианты использования?
Ответы
Ответ 1
Одним из слабых ссылок является реализация сменных функций в стандарте С++. А именно:
void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;
Это функции, которые должны быть реализованы реализацией, но если программа реализует их, то реализация программы заменяет или отменяет версию реализации. Это легко реализовать через слабую связь.
Ответ 2
В встроенной разработке, когда у вас есть, например, вектор указателей прерываний, очень удобно использовать слабую связь для получения обработчиков по умолчанию для прерываний, которые вам не интересны.
Это работает, определяя пустой обработчик (один раз), а затем вводит один новый правильно названный символ для каждого указателя прерывания, который вам нужен, который слабо связан с обработчиком по умолчанию.
Затем вектор заполняется этими символами, которые будут указывать на один и тот же фактический код, пока вы не решите реализовать один из них, используя одно и то же (правильное) имя, тогда ваш код "пересиливает" слабое звено, вызывая указатель на ваш код, который будет установлен в таблице прерываний.
Это часто реализуется в некоторой смеси C и сборки, но с использованием псевдокода C мы можем иметь что-то вроде:
static void placeholder_isr(void)
{
}
/* Introduce properly-named function pointers, with weak linking.
* NOTE: This syntax is completely fictional as far as I know.
*/
void (*timer1_isr)() = placeholder_isr __attribute("weak linking");
void (*timer2_isr)() = placeholder_isr __attribute("weak linking");
void (*usart1_isr)() = placeholder_isr __attribute("weak linking");
void (*usart2_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
/* Declare the table of interrupt handlers. */
static void (*isr_table)[] = {
timer1_isr,
timer2_isr,
usart1_isr,
usart2_isr,
dma1_isr,
dma2_isr,
} __attribute("isr vector"); /* Attribute to place it where it needs to go. */
Затем вы можете просто реализовать свою собственную функцию при необходимости:
void timer1_isr(void)
{
/* Handler ISR from timer1. */
}
не изменяя ничего, он "просто работает". До тех пор, пока ваше имя будет тем, что ожидает "код поддержки", указанный выше.
Ответ 3
Слабый атрибут заставляет объявление выдаваться как слабый а не глобальным. Это в первую очередь полезно при определении библиотечные функции, которые могут быть переопределены в коде пользователя, хотя это может также могут использоваться с не-функциональными объявлениями. Слабые символы поддерживается для целей ELF, а также для целей a.out при использовании GNU-сборщик и компоновщик.
Пример слабого атрибута:
weak.c
extern void foo() __attribute__((weak));
int main() {
if (foo) foo();
}
foo.c
void foo() {
printf("in foo.\n");
}
strong.c
extern void foo() ;
int main() {
if (foo) foo();
}
Компиляция
$ cc weak.c // Compiles OK
$ cc strong.c // undefined reference to `foo'
Когда "foo" объявляется слабым, его определение можно опустить, или заменены разными библиотеками, с видом "времени ссылки" связывание ". Линкер заполнит 0 для undefined слабых символов.
Ответ 4
Типичный и повседневный прецедент - это встроенные и шаблонные функции.
Например, эта часть кода при компиляции с g++ -shared -fPIC
:
extern void a();
inline void foo() { a(); }
void bar() { foo(); }
void baz() { foo(); }
Будет ли символ bar и baz отмечен как T (нормальный) на nm
, а foo будет отмечен как W - слабый.
(т.е. mn -C ./a.out
)
Обоснование:
встроенные функции и шаблоны могут быть определены в заголовках и обычно определяются несколько раз в разных частях источников и, наконец, только один остается активным.
Если он не будет отмечен как "Слабый", это будет столкновение нескольких "foo" символов или компилятор не сможет отключить вложение
Ответ 5
Обычно вы используете слабое соединение, когда хотите переопределить объявление функции в другой части вашего кода. Обычно это относится к библиотекам, которые указывают, например, обработчик ошибок по умолчанию, который вы можете переопределить с помощью специальной функции, если вы используете библиотеку.