LINUX.ORG.RU

Вложенные функции

 ,


0

2

Привет,

я столкнулся с тем, что такая (на мой взгляд) замечательная функция в gcc, как вложенные функции, которая позволяет улучшить стиль кода, мало используется. Более того она критикуется, например на stackoverflow. Среди доводов, которые я встречал, пожалуй, самым веским был тот факт, что использование встраиваемых функций в gcc приводит к тому, что стек получает атрибут исполняемого.

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



Последнее исправление: DesertFox (всего исправлений: 1)
Ответ на: комментарий от schizoid

http://habrahabr.ru/post/150868/ Здесь примеры не на Си, но то как автор решает свои проблемы, в Си могли бы быть решены вложенными функциями. У этих функций могли бы быть достаточно короткие имена. И если бы эти функции с короткими именами были бы не вложеными, то это бы засоряло пространство имен модуля, что в случае, если модуль большой может мешать.

DesertFox
() автор топика
Ответ на: комментарий от Dark_SavanT

массивы нулевого размера тоже не стандарт, к примеру. Да мало ли еще расширений, которые делают язык лучше?

DesertFox
() автор топика

Формально ничто не мешает реализовать это и без исполняемого стека. Собственно причина использования следующая:

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

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

Что-то подсказывает мне, что вызов/окончание могут быть не очень эффективны при отказе от использования стека. Впрочем можно попробовать иметь два «стека» для каждой из нитей.

io ★★
()
Последнее исправление: io (всего исправлений: 1)
Ответ на: комментарий от DesertFox

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

Dark_SavanT ★★★★★
()
Ответ на: комментарий от DesertFox

Не нашёл я там вложенных функций. Лямбды там есть, да, и они таки удобны, но это же C++0x, стандарт. Почему бы не пользоваться ими?

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

Дизайн, в общем.

schizoid ★★★
()

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

Нестандартное средство улучшает стиль кода? Это что-то новенькое.

мало используется

О вложенных функциях в gcc узнал ещё в 2.*.*-какой-то версии, то есть, давным-давно. За всё это время вложенные функции ни разу не понадобились. Отсюда вывод (типичный лоровский): не нужны.

А впрочем, если тебе соответствия стандарту не важны, то отчего же не использовать, хе-хе-хе, желаю успехов. :)

DeVliegendeHollander ★★
()
Ответ на: комментарий от schizoid

Почему бы не пользоваться ими?

Потому что я пишу на Си.

Вот пример. Было:

function locator($device) {
    // Can we locate the device?
    if ($device->token != "" && $device->expire <= $now) {
        return false;
    }

    $modelNotifier = new ModelNotifier($device); 
    return $modelNotifier->go();
}

Стало:

function ModelDevice::isLocatable() {
    return ($this->token != "" && $this->expire <= $now);
}

…

function locateAndNotifyDevice(ModelDevice $device) {
    if (!$device->isLocatable()) {
        return false;
    }

    $modelNotifier = new ModelNotifier($device);
    return $modelNotifier->go();
}

Но в Си я бы написал примерно так:

return_type locate_and_notify_device (model_device_t *device)
{
  int is_locatable (model_device_t *device)
  {
    return (strlen(device->token) &&
           (difftime(device->expire, time (NULL) > 0));
  }

  if (!is_locatable (device))
    return NULL;

  model_notifer_t *model_notifer = model_notifer_create (device);
  return_type ret = model_notifer->go();
  model_notifer_free (model_notifer);
  return ret;
}
DesertFox
() автор топика
Ответ на: комментарий от DeVliegendeHollander

А впрочем, если тебе соответствия стандарту не важны, то отчего же не использовать, хе-хе-хе, желаю успехов. :)

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

DesertFox
() автор топика
Ответ на: комментарий от DesertFox

Но в Си я бы написал примерно так:

Конечно с учетом, что функция is_locatable больше нигде не могла бы использоваться.

DesertFox
() автор топика
Ответ на: комментарий от buddhist

как-то даже и не знал. интересно, хот и не частоупотребимо.

Dark_SavanT ★★★★★
()
Последнее исправление: Dark_SavanT (всего исправлений: 1)
Ответ на: комментарий от DesertFox

Но в Си я бы написал примерно так

Если уж вложенными, тогда лучше без передачи параметра, иначе вся выгода теряется:

return_type locate_and_notify_device (model_device_t *device)
{
  int is_locatable ()
  {
    return (strlen(device->token) &&
           (difftime(device->expire, time (NULL) > 0));
  }
Пример плохой. Лучше сделать эту функцию доступной извне в любое время.

schizoid ★★★
()

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

Eddy_Em ☆☆☆☆☆
()

Полезность вложенных функций сомнительна, т.к. полноценных замыканий на них все равно не сделать.
Например, что будет если вернуть указатель на локальную функцию? Подозреваю, что он будет не валидный в случае если функция аллоцирована на стеке. А если аллоцировать в куче, без сборки мусора добавится дополнительная головная боль по ручному освобождению.
Поэтому, haskell/ocaml/whatever там, где нужна высокоуровневость, и си там, где нужна производительность.

rand
()

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

staseg ★★★★★
()

А в паскале они из коробки

unC0Rr ★★★★★
()
Ответ на: комментарий от staseg

Это конечно как вариант, но удобства уже меньше.

DesertFox
() автор топика
Ответ на: комментарий от DesertFox

Бог мой? Ты пишешь непереносимый код только для того чтобы вспомогательную функцию не могли вызвать где-то ещё кроме твоей функции? Что мешает тебе вынести и функцию и вспомогательные функции в одну отдельную единицу трансляции, а в заголовок поместить только объявление главной функции?

KblCb ★★★★★
()
Ответ на: комментарий от DesertFox

Какой итератор и qsort? У тебя C или C++?

KblCb ★★★★★
()
Ответ на: комментарий от DesertFox

Я про то, что вот вэтом конкретном примере внутреняя функция вообще не нужна, вот так будет проще:

return_type locate_and_notify_device (model_device_t *device)
{
	if (strlen(device->token) &&
         	difftime(device->expire, time (NULL) <= 0))
	{
		model_notifer_t *model_notifer = model_notifer_create (device);
		return_type ret = model_notifer->go();
		model_notifer_free (model_notifer);
		return ret;
	}
    return NULL;
}

А так, да, внутреняя функция полезна, если её код будет вызываться более одного раза, либо в ней инкапсулирована хоть какая-нибудь относительно сложная логика.

LamerOk ★★★★★
()

годное расширение. С аккуратностью можно использовать для изменения всяких call-back`ов. Например есть стандартная функция twalk, но в её call-back нельзя передать дополнительный указатель, но очень хочется и к тому-же семантика у него не слишком удобна :)

#include <search.h>

void preorder_walk(const void *root,
	void (*action)(const void *node,const int depth,void *udata),
	void *udata) {
	
	void internal_action(const void *node,const VISIT which,const int depth) {
		if (which==preorder || which == leaf) {
			action(node,depth,udata);
		}
	}
	
	twalk(root,internal_action);
}
на С без вложенных функций подобный фокус не реализуется

MKuznetsov ★★★★★
()

До лямбда-функций все равно далеко.

unfo ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.