Лучшая практика переопределения функции библиотеки?

У меня следующая ситуация. У меня есть корневой проект, который содержит подпроект. Этот подпроект задуман как библиотека, которую вы можете создать как отдельно, так и вместе с другим проектом.

В библиотеке мне нужно выделить память в нескольких местах. Однако я хотел бы иметь возможность переопределить эту функцию.

Например, в библиотеке у меня есть вызов malloc(ptr). И я хочу иметь возможность заменить эти вызовы потребителем библиотеки (в данном случае корневым проектом). В этом случае корневой проект должен переопределить/заменить вызовы в библиотеке без зависимости библиотеки от корневого проекта.

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

Мой вопрос сводится к следующему: Как лучше всего расширять подобные библиотеки?

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

На данный момент мне удалось достичь того, чего я хочу, это следующее.

  1. Я определил заголовок с макросом, заменяющим выделения, следующим образом: HDR_MALLOC(ptr) malloc(ptr). Вместо этого каждый malloc(ptr) в библиотеке теперь использует HDR_MALLOC(ptr). Когда библиотека создается автономно, она просто использует это определение.
  2. В корневом проекте я также определил заголовок с помощью макроса HDR_MALLOC(ptr).

Теперь при сборке библиотеки это работает правильно, корневой проект правильно «переопределяет» HDR_MALLOC библиотеки. Когда rootproject использует библиотеку, она вызывает реализацию корневого проекта вместо своей собственной.

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

J. Doe 04.09.2024 21:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Используйте указатели функций, например.

// lib.h
void libfoo(int one, double two, struct bar *three, void *(*alocfx)(size_t));

и в основном проекте вызовите библиотечную функцию

#include <stdlib.h>
#include "lib.h"
int main(void) {
    libfoo(1, 3.14159, NULL, malloc);
}

Вы можете реализовать библиотечную функцию так, что если указатель функции равен NULL, он будет использовать malloc() стандартной библиотеки. Конечно, добавьте это поведение в документацию библиотеки :)

pmg 04.09.2024 12:06

Спасибо за разъяснения. Я имел в виду, что внутри библиотеки (не доступной для основного проекта) вызывается функция (в данном случае malloc). Потребитель библиотеки просто вызывает открытые функции, которые внутренне вызывают функцию для переопределения. Сейчас я просто заменяю МАКРОС, определенный в библиотеке, на МАКРОС, определенный в корневом проекте. Таким образом, я «заменяю» все вызовы malloc, когда библиотека компилируется для проекта. Мне просто интересно, существует ли лучший, передовой способ, который также останется эффективным.

Sotem 04.09.2024 12:13

Замена MACRO внутри библиотеки происходит только в том случае, если вы компилируете ее снова и снова. Если вы скомпилируете свою библиотеку один раз (и свяжете ее с помощью gcc ... -lfoo), никакие изменения макросов в основном проекте не изменят поведение библиотеки.

pmg 04.09.2024 12:17

Я бы сказал, что указатель на функцию является более эффективной практикой, поскольку, хорошо, вы перекомпилируете библиотеку с проектом X, но как насчет времени выполнения, когда поиск библиотеки может зависеть от PATH, а как насчет того, когда у вас есть несколько подбиблиотек или частей, использующих foo.dll : может применяться одно «переопределение» макроса (в зависимости от того, как вы его загружаете, но это библиотека, поэтому она должна работать). Мне кажется, что с указателями fn этого риска можно избежать, поэтому «лучшая практика»

Aname 04.09.2024 12:23
Ответ принят как подходящий

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

#if defined(LIB_CONFIG_HEADER)
  #include LIB_CONFIG_HEADER
#else
  // Include some reasonable defaults
  #include <stdlib.h>
  ...
#endif
 
#if !defined(LIB_ALLOC)
  #define LIB_ALLOC(_size) calloc(1u, _size)
#endif

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

#define LIB_ALLOC(_size) ...
#define LIB_ALLOC_T(_type) ((_type) *)LIB_ALLOC(sizeof(_type))

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

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

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

Другие вопросы по теме