[Trick] Форсирование правильного использования библиотеки
От: remark Россия http://www.1024cores.net/
Дата: 21.01.07 11:47
Оценка: 12 (3)
Идём ещё дальше, чем здесь
Автор: remark
Дата: 21.01.07

Опять [M$ SPECIFIC]

Допустим есть заголовок библиотеки:
void lib_init();
void lib_f1();
void lib_f2();
void lib_deinit();


Мы хотим, что бы нельзя было:
1. вызывать lib_f1/lib_f2, если не вызвали lib_init
2. вызывать lib_deinit, если не вызвали lib_f1/lib_f2 и lib_init
3. не вызвать lib_deinit, если вызывали lib_f1/lib_f2/lib_init
4. вообще ничего не вызывать из заголовка (иначе зачем включали?)

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

lib_ind<0>            lib_init();
lib_ind<2, 0>        lib_f1();
lib_ind<2, 0>        lib_f2();
lib_ind<1, 0, 2>    lib_deinit();

static final_check<1> lib_use_check_;


Где первый параметр lib_ind — это номер группы, к которой относится функция. В данном случае имеем три группы: группа 0 — функция lib_init, группа 1 — функция деинициализации lib_deinit, группа 2 — "рабочие" функции библиотеки lib_f1/lib_f2.
Последующие параметры lib_ind — номера групп, из которых надо вызвать хотя бы одну функцию перед функциями из данной группы.
С помощью final_check можно задать группы, из которых безусловно должна быть вызвана хотя бы одна функция. В данном случае задаём, что обязательно должна быть вызвана функция lib_deinit (группа 1).
Т.е. таким образом задаём граф корректной последовательности вызовов функций.

Данное описание задаёт следующий порядок вызовов функций:

int main()
{
    lib_init();
    lib_f1();
    lib_f2();
    lib_deinit();
}


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

Далее код вспомогательных классов:

template<int id> struct lib_use
{
    typedef int fake;
};

static lib_use<-1> lib_use_;

template<int id, int check1, int check2> struct lib_helper
{
    typedef typename lib_use<id> fake;

#ifdef _MSC_VER
        __if_not_exists(lib_use<check1>)
        {
            BOOST_STATIC_ASSERT(false);
        }
        __if_not_exists(lib_use<check2>)
        {
            BOOST_STATIC_ASSERT(false);
        }
#endif
};

template<int id, int check1 = -1, int check2 = -1> class lib_ind
{
    typedef typename lib_helper<id, check1, check2>::fake fake;
};

template<int id> struct final_check
{
    final_check()
    {
#ifdef _MSC_VER
        __if_not_exists(lib_use<id>)
        {
            BOOST_STATIC_ASSERT(false);
        }
#endif
    }
};



Далее некоторые замечания:
Задаётся порядок именно упоминаний функций в коде (порядок вызовов в райн-тайм проверить достаточно тривиально). Следовательно скомпилируется следующий (некорректный) код:

void init()
{
    lib_init();
}

void f()
{
    lib_f1();
}

void main2()
{
    f();
    init();
    lib_deinit();
}


Ну тут мы делаем по крайне мере не хуже чем было.
И в тоже время не скомпилируется следующий (корректный) код:

void f()
{
    lib_f1();
}

void init()
{
    lib_init();
}

void main2()
{
    init();
    f();
    lib_deinit();
}


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

О практической применимости такой техники в реальной жизни решайте сами.

В защиту могу сказать следующее. Такая техника заставляет:
1. Локализовывать использование библиотеки в одном cpp-файле. Размазывание вызовов по нескольким файлам в общем случае, я бы считал Плохой Вещью.
2. Форсируется использование библиотеки в виде некого заранее известного, корректного и локального паттерна кода. Например так:
int main()
{
    lib_init();
    lib_f1();
    lib_f2();
    lib_deinit();
}

Понятно, что можно и корректно использовать библиотеку, если разнести вызовы по разным функциям и перемешать их в хаотическом порядке. Но зачем? Если можно записать их в одной функции и в известном порядке. Т.о. если открыть файл, который видишь первый раз, человека, код которого ни разу не видел, всё равно будешь знать что ожидать.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Форсирование правильного использования библиотек
От: IROV..  
Дата: 21.01.07 14:01
Оценка: 1 (1)
Здравствуйте, remark, Вы писали:




я не волшебник, я только учусь!
Re: [Trick] Форсирование правильного использования библиотек
От: Константин Л. Франция  
Дата: 21.01.07 15:09
Оценка:
remark, ды ты маньяк!
Re[2]: [Trick] Форсирование правильного использования библио
От: IROV..  
Дата: 21.01.07 15:14
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>remark, ды ты маньяк!


да я бы за такой то код, да по самые гланды!
я не волшебник, я только учусь!
Re: [Trick] Форсирование правильного использования библиотек
От: Tonal- Россия www.promsoft.ru
Дата: 21.01.07 15:26
Оценка: 10 (1) +1
Здравствуйте, remark, Вы писали:
R>Допустим есть заголовок библиотеки:
R>
R>void lib_init();
R>void lib_f1();
R>void lib_f2();
R>void lib_deinit();
R>


R>Мы хотим, что бы нельзя было:

R>1. вызывать lib_f1/lib_f2, если не вызвали lib_init
R>2. вызывать lib_deinit, если не вызвали lib_f1/lib_f2 и lib_init
R>3. не вызвать lib_deinit, если вызывали lib_f1/lib_f2/lib_init
R>4. вообще ничего не вызывать из заголовка (иначе зачем включали?)
R>
Круто конечно, но, почему просто не сделать так:

struct lib {
  lib() {lib_init();}
  ~lib() {lib_deinit();}
  void lib_f1() {lib_f1();}
  void lib_f2() {lib_f2();}
};

Кроме пункта 4 — всё выполняется.
Тебе не кажется, что ты изобрёл классы?

Насчёт пункта 4 — оно конечно да, при включении лишнего файла увеличивается время компиляции, но не окажется ли, что эти навороты, если их применять повсеместно, съедят гораздо больше того-же самого времени?
Причём, неиспользуемый заголовок мы таки можем найти и выкинуть, чем избавимся от этих затрат.
А твоя механика будет жрать ресурсы всегда.
Ну и не работает в случае заголовков из которых используются только типы stdint.h например.
Re[3]: [Trick] Форсирование правильного использования библио
От: Константин Л. Франция  
Дата: 21.01.07 15:41
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>Здравствуйте, Константин Л., Вы писали:


КЛ>>remark, ды ты маньяк!


IRO>да я бы за такой то код, да по самые гланды!


а ты извращенец
Re[2]: [Trick] Форсирование правильного использования библио
От: remark Россия http://www.1024cores.net/
Дата: 21.01.07 16:25
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Круто конечно, но, почему просто не сделать так:


T>
T>struct lib {
T>  lib() {lib_init();}
T>  ~lib() {lib_deinit();}
T>  void lib_f1() {lib_f1();}
T>  void lib_f2() {lib_f2();}
T>};
T>

T>Кроме пункта 4 — всё выполняется.
T>Тебе не кажется, что ты изобрёл классы?

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


T>Насчёт пункта 4 — оно конечно да, при включении лишнего файла увеличивается время компиляции, но не окажется ли, что эти навороты, если их применять повсеместно, съедят гораздо больше того-же самого времени?

T>Причём, неиспользуемый заголовок мы таки можем найти и выкинуть, чем избавимся от этих затрат.
T>А твоя механика будет жрать ресурсы всегда.

Ты не учёт ещё один важный момент: зависимости между файлами — если у тебя в cpp включён десяток лишних h, то и пересобиратся он будет при изменении этих h'ников. Бывают такие проекты, где пара сотен файлов и они все всегда пересодираются при любом изменении и


T>Ну и не работает в случае заголовков из которых используются только типы stdint.h например.


Для таких заголовков техника не применима... хотя если подумать



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: [Trick] Форсирование правильного использования библио
От: Tonal- Россия www.promsoft.ru
Дата: 21.01.07 17:41
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Tonal-, Вы писали:


R>Предложенный способ позволяет гораздо больший набор, практически произвольный.

Класс тоже позволяет произвольный, и не позволяет например такой:
lib_init();
lib_deinit();
lib_deinit();

или такой
lib_init();
lib_deinit();
lib_f1();


Зато позволяет провести корректный повторный цикл.

R>Ты не учёт ещё один важный момент: зависимости между файлами — если у тебя в cpp включён десяток лишних h, то и пересобиратся он будет при изменении этих h'ников. Бывают такие проекты, где пара сотен файлов и они все всегда пересодираются при любом изменении и

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

T>>Ну и не работает в случае заголовков из которых используются только типы stdint.h например.

R>Для таких заголовков техника не применима... хотя если подумать
Не только их.
Что делать с библиотеками классов и шаблонов?
Что делать с библиотеками макросов?

Кроме того, есть техники, основанные на неоднократном включении заголовков см. Flex или Boost.Preprocess.
Ну и кроме того всёж-таки MS...
Хотя, у gсс, насколько я знаю, свои навароты имеються схожей тематики
Re[4]: [Trick] Форсирование правильного использования библио
От: remark Россия http://www.1024cores.net/
Дата: 21.01.07 18:03
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Что делать с библиотеками классов и шаблонов?

T>Что делать с библиотеками макросов?
T>Кроме того...

Я не говорил, что этим решением я собираюсь решить все проблемы программирования


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: [Trick] Форсирование правильного использования библио
От: remark Россия http://www.1024cores.net/
Дата: 21.01.07 18:06
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Tonal-, Вы писали:


T>>Что делать с библиотеками классов и шаблонов?

T>>Что делать с библиотеками макросов?
T>>Кроме того...

R>Я не говорил, что этим решением я собираюсь решить все проблемы программирования


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

R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Форсирование правильного использования библиотек
От: Кодт Россия  
Дата: 22.01.07 09:24
Оценка:
Здравствуйте, remark, Вы писали:

R>Допустим есть заголовок библиотеки:

R>
R>void lib_init();
R>void lib_f1();
R>void lib_f2();
R>void lib_deinit();
R>


О какой библиотеке идёт речь?
— инлайновая
— статическая
— динамическая

Фокус с шаблонами (запрос к линкеру, что именно было использовано хоть где-то) прокатит только для первого случая и только в пределах одной единицы трансляции.
Действительно ли оно надо? Это ведь не просто ограничение. Будут ложные срабатывания, если lib_init делается в одном .cpp, а lib_deinit — в другом.

Может быть, более практично делать проверки в рантайме, написав простенький конечный автомат.
Вот придумать на DSL для определения такого автомата — это было бы прикольно.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: [Trick] Форсирование правильного использования библио
От: remark Россия http://www.1024cores.net/
Дата: 22.01.07 20:25
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, remark, Вы писали:


R>>Допустим есть заголовок библиотеки:

R>>
R>>void lib_init();
R>>void lib_f1();
R>>void lib_f2();
R>>void lib_deinit();
R>>


К>О какой библиотеке идёт речь?

К>- инлайновая
К>- статическая
К>- динамическая

Неважно — h'ник будет в любом случае.

К>Фокус с шаблонами (запрос к линкеру, что именно было использовано хоть где-то) прокатит только для первого случая и только в пределах одной единицы трансляции.


Так и есть.

К>Действительно ли оно надо?


Я это и не утверждаю

К>Это ведь не просто ограничение. Будут ложные срабатывания, если lib_init делается в одном .cpp, а lib_deinit — в другом.


Да. Все правильно. Об этом и писал. А зачем разносить работу с библиотекой по разным файлам, если её вполне можно использовать в одном?
Ну опять же, надо к этому правильно относится — как тут один товарищь правильно назвал это ресёрчем


К>Может быть, более практично делать проверки в рантайме, написав простенький конечный автомат.

К>Вот придумать на DSL для определения такого автомата — это было бы прикольно.

Имхо не прикольно — имхо банально...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Форсирование правильного использования библиотек
От: Хитрик Денис Россия RSDN
Дата: 22.01.07 20:36
Оценка:
Здравствуйте, remark, Вы писали:

R>2. Форсируется использование библиотеки в виде некого заранее известного, корректного и локального паттерна кода. Например так:

R>
R>int main()
R>{
R>    lib_init();
R>    lib_f1();
R>    lib_f2();
R>    lib_deinit();
R>}
R>

R>Понятно, что можно и корректно использовать библиотеку, если разнести вызовы по разным функциям и перемешать их в хаотическом порядке. Но зачем? Если можно записать их в одной функции и в известном порядке. Т.о. если открыть файл, который видишь первый раз, человека, код которого ни разу не видел, всё равно будешь знать что ожидать.

Если это паттерн, то почему бы его не сделать простой функцией библиотеки и не парить мозги компилятору?

Конечно, впечатляет. Но с IROV я соглашусь — в реальных проектах за такое руки отрывать надо
Правила нашего с вами форума.
Как правильно задавать вопросы. © 2001 by Eric S. Raymond; перевод: © 2002 Валерий Кравчук.
Re[2]: [Trick] Форсирование правильного использования библио
От: remark Россия http://www.1024cores.net/
Дата: 22.01.07 20:38
Оценка:
Здравствуйте, Хитрик Денис, Вы писали:

ХД>Если это паттерн, то почему бы его не сделать простой функцией библиотеки и не парить мозги компилятору?


Встречный вопрос: почему всё win api не сделали одной большой функцией, принимающей много-много параметров?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: [Trick] Форсирование правильного использования библио
От: Хитрик Денис Россия RSDN
Дата: 22.01.07 20:47
Оценка:
Здравствуйте, remark, Вы писали:

ХД>>Если это паттерн, то почему бы его не сделать простой функцией библиотеки и не парить мозги компилятору?

R>Встречный вопрос: почему всё win api не сделали одной большой функцией, принимающей много-много параметров?

Намекаешь на то, что разработчики не знали С++?
Правила нашего с вами форума.
Как правильно задавать вопросы. © 2001 by Eric S. Raymond; перевод: © 2002 Валерий Кравчук.
Re: [Trick] Форсирование правильного использования библиотек
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 21:02
Оценка:
Здравствуйте, remark, Вы писали:

R>Идём ещё дальше, чем здесь
Автор: remark
Дата: 21.01.07

R>Опять [M$ SPECIFIC]

Если кого это интересует, то это больше не M$ SPECIFIC, это 100% PURE C++ благодаря здесь
Автор: remark
Дата: 06.02.07



R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.