LINUX.ORG.RU

Возврат указателя на вложенную функцию.

 ,


2

4

Сейчас проектирую API для *****, (тут было много буков, но я всё стёр). И возникла мысль что довольно много функций по сути нужны только один раз и в одном единственном месте, но их надо проверять/тестировать, а для этого либо объявлять их в заголовочных файлах, но это замусоривает их, либо объявлять в исходниках тестов, но я иногда меняю именования целых блоков функций после тестового прогона в виде юзабилити написания на этом API. Короче подумалось что по сути можно использовать вложенные функции и не просто вызывать их внутри основной, а возвращать и вызывать уже в тесте отсекая ошибки которые могли бы возникнуть если бы я делал по иному то есть вызывал бы вложенную функцию через какойнить аргумент «родительской» типа так

#include <stdio.h>
#include <stdbool.h>
void foo(bool flag)
{
    void bar()
    {
        printf("hello\n");
    }
    void mar()
    {
       printf("world\n");
    }
    (flag)?bar():mar();

}
int main()
{
   foo(true);  // вызов bar();
   foo(false); // вызов mar();
   return 0;
}

А подумал что будет довольно удобно вот так

#include <stdio.h>
#include <stdbool.h>
void * foo(bool opt)
{
  void(*p)() = NULL;
  void one()
  {
    printf("some work one\n");
  }
  void twoo()
  {
    printf("some work twoo\n");
  }
    return (opt)?(p=one):(p=twoo);
}

int main()
{
    void(*func)() = NULL;
    func = foo(true);
    func();
    func = foo(false);
    func();
    return 0;
}

Но я плохо понимаю как это в памяти будет, как бы функция в стеке и вложенная тоже, что если у меня их будут сотни и внутри вложенных будут static переменные/структуры/объединения, а то и массивы. Вообщем вопрос, что я могу огрести от подобного? (От второго варианта с возвратом указателя на вложенную функцию)

Компилироваться всё это бeдет для linux/windows/android/darwin/wasm

Deleted

то что функция вложенная будет видно только в коде. Это для красоты. А так - функция как функция

mittorn ★★★★★
()

Документация немного странно описывает этот момент:

If you try to call the nested function through its address after the containing function exits, all hell breaks loose. If you try to call it after a containing scope level exits, and if it refers to some of the variables that are no longer in scope, you may be lucky, but it’s not wise to take the risk. If, however, the nested function does not refer to anything that has gone out of scope, you should be safe.
Без захвата локальных переменных, видимо, может работать.

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

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

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

ну нюанс есть - вложенные функции - расширение gcc. И не нужно их использовать без необходимости.
вот в c++ можно сделать вложенный класс или использовать лямбды. Там с этим проблем не будет. Да и поведение лямбд более чётко оговорено.

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

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

Deleted
()

Keep it simple, stupid.

anonymous
()
Ответ на: комментарий от Deleted

Вот наглядный пример:
https://ideone.com/aiUlVO
Тут я школоте объяснял про то как оно в памяти:

Дмитрий 26 июл
http://ideone.com/aiUlVO
Дмитрий 26 июл
немного про указатели
Дмитрий 26 июл
a - переменная, &a - адрес переменной в памяти
Дмитрий 26 июл
%p выводит указатели в шестнадцатеричном виде
Дмитрий 26 июл
как видно, в примере выше по адресам 0xbfXXXX расположились переменные, определённые внутри функции
Дмитрий 26 июл
0x80XXX - статическая память. символы в строке расположены там. статические переесменные и массивы, глобальные переменные и массивы там же
Дмитрий 26 июл
динамическая память (куча) оказалась в 0x86XXXXX
Дмитрий 26 июл
ну и в конце видно: указатель на массив - то же самое, что и указатель на первый элемент
Дмитрий 26 июл
ну и если брать массив без знака & - в си всегда берётся указатель на первый элемент
Дмитрий 26 июл
так только для статического массива
Дмитрий 26 июл
для динамического & возьмёт адрес переменной, в которой хранится указатель на первый элемент

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

Спасибо всегда забываю обращать внимание на «префиксы» начальные значения адресов что бы понимать в каких «общих» областях лежат те или иные виды типов. Мне хоть в лоб, хоть по лбу, хоть бумажку перед носом вешай «Зри в адрес оби-ван кеноби х**в» ::)

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)

Всем спасибо.

Deleted
()

Не нужно(по крайне мере для озвученных целей). Используй static функции(т. е. локальные в рамках модуля) коль скоро они нужны в ограниченном контексте. Ситуацию с тестами препроцессором обрулить можно

Deleted
()
Ответ на: комментарий от E

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

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

Да я не спорю, можно конечно. Буду думать. И взвешивать все за и против.

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

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

Но тогда и смысла нет юзать такое несовместимое расширение.

Наверняка это касается и static переменных в области видимости родительской функции

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

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

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

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

Но тогда и смысла нет юзать такое несовместимое расширение.

Я хотел переменные брать из отдельной функции где они определены как static то есть у меня изолированные данные доступные через одну функцию и изолированный (внутренний api) оторый доступен только через вызов другой функции, тоесть изоляция как данных так и «методов»

Типа так (псевдокод)


type data_func(value_type,value data)
{
  static type a =bla;
  static type b =bla;
  ...

    switch(value_type)
    {
       case A_TYPE:(data == NO_DATA)a=data:return a; break;
       case B_TYPE:(data == NO_DATA)b=data:return b; break;
       ...
       default:break;
    }
}



type interface_func(flag)
{
    internal_func_one()
    {
       type var1 = data_func(A_TYPE,NO_DATA);
       type var2 = data_func(B_TYPE,NO_DATA);
       ...
       data_func(C_TYPE,42);
    }

    if(flag)
    {
       return internal_func_one;
    };
    .... blablabla

}


Ну не прямо так, но смысл думаю прояснил. Вызвать одну процедуру можно только посредством вызова другой, получить изменить данные можно только посредством вызова ещё одной.

Deleted
()
Ответ на: комментарий от E

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


struct
{
  int (*get_size_x)(void);
  void(*set_size_x)(int);
  int blala;
}inter_face;


struct inter_face interface()
{
   int  get_size_x(void)  {где  то берём  x};
   void set_size_x(int x) {куда то отдаём x};
   struct inter_face face = {get_size_x,get_size_y};
   return face;
}


int x_size = interface().get_size_x();
interface().set_size_x(42);
interface().blabla= 84;


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

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

А сейчас ты пытаешься сделать C++ из Pure C.

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

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

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

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

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

критического НЕТ не было

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

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

То, что ты «проектируешь API» для чего-то, linux/windows/android/darwin/wasm, и при этом задаешь такие вопросы, означает... вот то, что я написал.

А interface().get_size_x() - это просто песня. Функция без аргументов, вызываемая через вектор операций, и возвращающая значение.

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

Я когда краши в dll загруженной загрузчиком дллок из mplayer отлаживал - как раз научился по адресам разную память различать. Теперь на автомате уже. Главное чтобы были образцы на которые положиться в текущем процессе.

mittorn ★★★★★
()

Следуй инструкции:

  • Выдохни
  • Попей и покушай
  • Используй приватные хидеры в тестах, просто не клади их в пакет вместе с публичными
pon4ik ★★★★★
()

а для этого либо объявлять их в заголовочных файлах, но это замусоривает их

А кто мешает сделать приватный хедер для таких вещей?

XMs ★★★★★
()

А мне кажется удобно так:

#define foo(flag) printf("%s\n",(flag)?"hello":"world")

anonymous
()

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

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

Лет десять назад эти функции реализовывались в гцц через трамплин на стеке, так что это равносильно {char a[16]; return a}, ((...)a)(). Как сейчас не знаю, но скорее всего это все еще уб, даже если ты не захватываешь контекст. Тред до конца не читал.

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