Как я могу проверить, что все мои функции init были вызваны?

Это не касается Вашего данного примера, но так как Вы упомянули, что действительно интересуетесь shared_ptr поведение при удалении его находящегося в собственности объекта, Вы могли бы интересоваться использованием shared_ptr 'средство удаления'.

, Если объект, принадлежавший shared_ptr потребности специальная обработка, будучи удаленным, можно определить 'средство удаления' для какого-либо конкретного shared_ptr<>. Средство удаления не является частью типа, это - атрибут shared_ptr<> экземпляр, таким образом, Ваш контейнер shared_ptr<> объекты мог иметь некоторые объекты с различными средствами удаления. Вот то, что документы Повышения говорят о shared_ptr<> средство удаления:

Пользовательские deallocators позволяют функции фабрики возврат shared_ptr для изоляции пользователя от его стратегии выделения памяти. Так как deallocator не является частью типа, изменение стратегии выделения не повреждает источник или совместимость на уровне двоичных кодов, и не требует клиентской перекомпиляции. Например, "нет" deallocator полезен при возврате shared_ptr к статически выделенному объекту, и другие изменения позволяют shared_ptr использоваться в качестве обертки для другого интеллектуального указателя, упрощая совместимость.

Это было бы самым чистым, если Вы могли бы изменить IFoo для имения виртуального деструктора, так как Вы планируете удалить объекты, которые являются подклассами его через IFoo ссылка или указатель. Но если Вы застреваете с IFoo, который не может быть исправлен, тогда если Вы хотите использовать shared_ptr<IFoo> в Вашем контейнере, но иметь его указывающий Bar, Вы могли создать shared_ptr экземпляр со средством удаления, которое работает, удрученное к Bar* тогда делает удалить операцию. Downcasts считают невоспитанностью, но эта техника могла бы быть чем-то, что Вы могли использовать в связывании.

5
задан NXT 28 September 2009 в 07:58
поделиться

8 ответов

Я не знаю, как уродливо выглядит следующее, но все равно публикую :-)

(Основная идея состоит в том, чтобы зарегистрировать указатели функций, например, что atexit функция выполняет.
Конечно, реализация atexit отличается)

В основном модуле у нас может быть что-то вроде этого:

typedef int (*function_t)(void);

static function_t  vfunctions[100]; // we can store max 100 function pointers
static int         vcnt = 0; // count the registered function pointers

int add2init(function_t f)
{
  // todo: error checks
  vfunctions[vcnt++] = f;
  return 0;
}
...

int main(void) {
 ...
 // iterate vfunctions[] and call the functions
 ...
}

... и в каком-то другом модуле:

typedef int (*function_t)(void);
extern int add2init(function_t f);
#define M_add2init(function_name)  static int int_ ## function_name = add2init(function_name)

int foo(void)
{
   printf("foo\n");
   return 0;
}
M_add2init(foo); // <--- register foo function
1
ответ дан 14 December 2019 в 01:12
поделиться

Вы можете использовать расширение gcc __ attribute __ ((constructor)) , если gcc подходит для вашего проекта.

#include <stdio.h>
void func1() __attribute__((constructor));
void func2() __attribute__((constructor));

void func1()
{
    printf("%s\n",__func__);
}

void func2()
{
    printf("%s\n",__func__);
}

int main()
{
    printf("main\n");
    return 0;
}

//the output
func2
func1
main
3
ответ дан 14 December 2019 в 01:12
поделиться

Если ваш отдельный модуль представляет сущность «класса» и имеет конструктор экземпляра, вы можете использовать следующую конструкцию:

static inline void init(void) { ... }
static int initialized = 0;
#define INIT if (__predict_false(!initialized)) { init(); initialized = 1; }
struct Foo *
foo_create(void)
{
    INIT;
    ...
}

где « __ pred_false » - подсказка для предсказания ветвления вашего компилятора. Когда создается первый объект, модуль инициализируется автоматически (однократно).

1
ответ дан 14 December 2019 в 01:12
поделиться

Splint (и, возможно, другие варианты Lint) могут выдавать предупреждение о функциях, которые определены, но не вызваны.

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

1
ответ дан 14 December 2019 в 01:12
поделиться

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

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

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

//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;

void moduleInit(void)
{
// so init here
}

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

тогда вы можете получить доступ к начальному и конечному адресам раздела из основного. и идти оттуда.

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

это, по сути, создание вашей собственной статической системы конструкторов на C.

, если вы делаете большой проект, и ваш компоновщик, по крайней мере, не является полнофункциональным, у вас может быть проблема ...

1
ответ дан 14 December 2019 в 01:12
поделиться

Могу я дать ответ на свой вопрос?

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

Затем я просматривал таблицу символов, созданную -gstab, и искал любые функции с именем init_ *, которые не были вызваны.

Это встроенное приложение, поэтому у меня есть изображение эльфа во флэш-памяти.

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

0
ответ дан 14 December 2019 в 01:12
поделиться

Почему бы не написать сценарий постобработки, который бы проверил за вас. Затем запустите этот сценарий как часть процесса сборки ... Или, еще лучше, сделайте его одним из ваших тестов. Вы ведь пишете тесты? :)

Например, если у каждого из ваших модулей есть файл заголовка, modX.c. И если подпись вашей функции init () - "void init ()" ...

Попросите ваш скрипт grep просмотреть все ваши .h-файлы и создать список имен модулей, которые нужно редактировать с помощью init (). Затем пусть скрипт проверит, действительно ли init () вызывается для каждого модуля в main ().

1
ответ дан 14 December 2019 в 01:12
поделиться

Большее время работы не проблема

Вы можете реализовать своего рода «конечный автомат» для каждого модуля, в котором действия функции зависят от состояния, в котором находится модуль. . Это состояние может быть установлено на BEFORE_INIT или INITIALIZED.

Например, допустим, у нас есть модуль A с функциями foo и bar.

Фактическая логика функций (то есть, что они на самом деле do) будет объявлен так:

void foo_logic();
void bar_logic();

Или какова бы ни была его подпись.

Затем фактические функции модуля (т. е. фактическая функция, объявленная foo ()), будут выполнять проверку во время выполнения условия модуля, и решите, что делать:

void foo() {
       if (module_state == BEFORE_INIT) {
           handle_not_initialized_error();
       }
       foo_logic();
}

Эта логика повторяется для всех функций.

Несколько замечаний:

  1. Это, очевидно, приведет к огромным потерям производительности, поэтому наверное, не очень хорошая идея (я опубликовал в любом случае, потому что вы сказали, что время выполнения не проблема).
  2. Это не настоящий конечный автомат, поскольку есть только два состояния, которые проверяются с использованием базового if, без какой-либо интеллектуальной общей логики.
  3. Это своего рода "шаблон проектирования" отлично работает, когда вы используете отдельные потоки / задачи, а функции, которые вы вызываете, на самом деле вызываются с использованием какого-то типа IPC.
  4. Конечный автомат может быть прекрасно реализован на C ++, возможно, стоит прочитать об этом. Подобная идея может быть запрограммирована на C с помощью массивов указателей на функции, но это почти наверняка не стоит вашего времени.
1
ответ дан 14 December 2019 в 01:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: