gui_moderator (gui_moderator) wrote in gui_programming,
gui_moderator
gui_moderator
gui_programming

C++ Tricks: Функция в функции

Никогда не жалел о том, что в свое время перешел с Паскаля на С++. Лишь двух вещей мне нехватало в С++: оператора with .. do и возможности определять локальные функции. К первому я привык, а вторую проблему оказалось возможным элегантно решить с помощю unnamed-структур.

Рассмотрим следующую задачу: у нас есть окна, с которыми связаны массивы данных. В одной из функци нашей необъятной программы мы должны в нескольких местах проверять наличие этих данных и освобождать занимаемую ими память. Вот так:
LPBYTE pData = (LPBYTE)GetWindowLongPtr(hWnd, GWL_USERDATA);
if(pData)
{
    delete [] pData;
    SetWindowLongPtr(hWnd, GWL_USERDATA, NULL);
}
Не хочется в двух или более местах писать один и тот же код. Самое очевидное решение - определить вспомогательную функцию, с помощью которой и выполнять эти нехитрые манипуляции. Действительно, можно использовать anonymous namespace и спрятать нашу функцию от внешнего мира подобным образом. Можно объявить эту функцию как private static член класса и остаться довольным. Однако, оба эти способа все же делают нашу вспомогательную функцию доступной не только вызывающей ее функции. Лучше поступить следующим образом:
void foo()
{
   struct
   {
      void operator()(HWND hWnd)
      {
	 LPBYTE pData = (LPBYTE)GetWindowLongPtr(hWnd, GWL_USERDATA);
	 if(pData)
	 {
	    delete [] pData;
	    SetWindowLongPtr(hWnd, GWL_USERDATA, NULL);
	 }
      }
    } 
    function;

    .....
    .....
    function(hWnd1);
    .....
    function(hWnd2)
    .....
    function(hWnd3);
    .....
    .....
}
Заметьте, структура не имеет имени и единственный объект этого неименнованного типа используется как функция. Получилась локальная функция, которой иногда так нехватает!

П.С. Строка (LPBYTE)GetWindowLongPtr(hWnd, GWL_USERDATA); генерирует warning C4312. О том как элегантно избавться от него, я расскажу в одном из следующих постов.
Tags: c++
  • Post a new comment

    Error

    default userpic
  • 8 comments
>with .. do

some[long].expression.a = 1
some[long].expression.b = 2
some[long].expression.c = 3

SomeType &s = some[long].expression;
s.a = 1;
s.b = 2;
s.c = 3;

К сожалению, у локальных функций в C/C++ нет доступа к переменным объемлющей функции, из-за чего они довольно громоздкие и неудобные.
+1 Фактически в C++ нет локальных (в функции) функций и классов, поскольку якобы_локальным функциям и классам не передается никакой контекст, отчего они становятся не более полезными, чем поименованные члены анонимного namespace'а. Кстати, предложенное выше решение вносит в программу такое каличество синтаксического мусора, что привносит больше проблем, чем решает.
Наверное ты еще больше НЕ пожалеешь, если попробуешь вкус Java. Там долбаться так не нужно.
Наверное :)
у нас есть окна, с которыми связаны массивы данных. В одной из функци нашей необъятной программы мы должны в нескольких местах проверять наличие этих данных и освобождать занимаемую ими память

Первое, что должно прийти в голову при такой проблеме — мысль «у меня пропущена какая-то сущность!». Распространенные дефекты в объектном дизайне часто именно так и проявляются. Кстати, само присутствие кода типа 'delete ptr' или (еще хуже) 'delete []arr' в высокоуровневой части программы говорит о том же и еще более четко. При правильном объектном дизайне все такие действия выполняются сами собой в конструкторах/деструкторах стековых оберток или (в более сложных случаях) объектами-прокси.
Ну почему сразу сущность. Например если надо сделать простой механизм сабклассинга, то в юзердате очень удобно хранить указательно на класс-обертку и делегировать ему вызов WindowProc из статической функции.
Ну а delete []arr, это, конечно, не совсем хорошо.
Вы привОдите пример который в любой разумной системе будет находиться на самом низком уровне т.е. будучи однажды реализован, проживет в неизменном виде годы и годы. Таких мест очень немного и они весьма специфичны. Я же говорил о более привычных задачах, в которых перечисленные симптомы часто сигнализируют о пропущенной сущности.
Да, я просто со своей колокольни несколько в иную сторону смотрю. Мои привычные задачи, как правило, именно на таком низком уровне и находятся, иногда вырождаясь в перехват вызовов API.