'callback'에 해당되는 글 2건

  1. 2011.11.15 Hollywood Principle
  2. 2011.09.08 Callback 함수 튜토리얼

Hollywood Principle

Posted by epicdev Archive : 2011. 11. 15. 15:55
From Wikipedia, the free encyclopedia

In computer programming, the Hollywood Principle is stated as "don't call us, we'll call you." It has applications in software engineering; see also implicit invocation for a related architectural principle.

[edit]Overview

The Hollywood principle is a software design methodology that takes its name from the cliché response given to amateurs auditioning in Hollywood: "Don't call us, we'll call you". It is a useful paradigm that assists in the development of code with high cohesionand low coupling that is easier to debug, maintain and test.

Most beginners are first introduced to programming from a diametrically opposed viewpoint. Programs such as Hello World take control of the running environment and make calls on the underlying system to do their work. A considerable amount of successful software has been developed using the same principles, and indeed many developers need never think there is any other approach. After all, programs with linear flow are generally easy to understand.

As systems increase in complexity, the linear model becomes less maintainable. Consider for example a simple program to bounce a square around a window in your favorite operating system or window manager. The linear approach may work, up to a point. You can keep the moving and drawing code in separate procedures, but soon the logic begins to branch.

  • What happens if the user resizes the window?
  • Or if the square is partially off-screen?
  • Are all those system calls to get such resources as device contexts and interacting with the graphical user interface really part of the solution domain?

It would be much more elegant if the programmer could concentrate on the application (in this case, updating the coordinates of the box) and leave the parts common to every application to "something else".

The key to making this possible is to sacrifice the element of control. Instead of your program running the system, the system runs your program. In our example, our program could register for timer events, and write a corresponding event handler that updates the coordinates. The program would include other callbacks to respond to other events, such as when the system requires part of a window to be redrawn. The system should provide suitable context information so the handler can perform the task and return. The user's program no longer includes an explicit control path, aside from initialization and registration.

Event loop programming, however, is merely the beginning of software development following the Hollywood principle. More advanced schemes such as event-driven object-orientation go further along the path, by software components sending messages to each other and reacting to the messages they receive. Each message handler merely has to perform its own local processing. It becomes very easy to unit test individual components of the system in isolation, while integration of all the components typically does not have to concern itself excessively with the dependencies between them.

Software architecture that encourages the Hollywood principle typically becomes more than "just" an API - instead, it may take on more dominant roles such as a software framework or container. Examples:

All of these mechanisms require some cooperation from the developer. To integrate seamlessly with the framework, the developer must produce code that follows some conventions and requirements of the framework. This may be something as simple as implementing a specific interface, or, as in the case of EJB, a significant amount of wrapper code, often produced by code generation tools.

[edit]Recent paradigms

More recent paradigms and design patterns go even further in pursuit of the Hollywood principle. Inversion of control for instance takes even the integration and configuration of the system out of the application, and instead performs dependency injection.

Again, this is most easily illustrated by an example. A more complex program such as a financial application is likely to depend on several external resources, such as database connections. Traditionally, the code to connect to the database ends up as a procedure somewhere in the program. It becomes difficult to change the database or test the code without one. The same is true for every other external resource that the application uses.

Various design patterns exist to try to reduce the coupling in such applications. In the Java world, the Service locator pattern exists to look up resources in a directory, such as JNDI. This reduces the dependency - now, instead of every separate resource having its own initialization code, the program depends only on the service locator.

[edit]Inversion of control

Inversion of control containers take the next logical step. In this example, the configuration and location of the database (and all the other resources) is kept in a configuration file external from the code. The container is responsible for resolution of these dependencies, and delivers them to the other software components - for example by calling a setter method. The code itself does not contain any configuration. Changing the database, or replacing it with a suitable mock object for unit testing, becomes a relatively simple matter of changing the external configuration. Integration of software components is facilitated, and the individual components get even closer to the Hollywood principle. 
  

Callback 함수 튜토리얼

Posted by epicdev Archive : 2011. 9. 8. 01:15

출처: http://www.codeguru.com/cpp/cpp/cpp_mfc/callbacks/article.php/c10557

Callback Functions Tutorial



Introduction

If you are reading this article, you probably wonder what callback functions are. This article explains what callback functions are, what are they good for, why you should use them, and so forth. However, before learning what callback functions are, you must be familiar with function pointers. If you aren't, consult a C/C++ book or consider reading the following:

What Is a Callback Function?

The simple answer to this first question is that a callback function is a function that is called through a function pointer. If you pass the pointer (address) of a function as an argument to another, when that pointer is used to call the function it points to it is said that a call back is made.

Why Should You Use Callback Functions?

Because they uncouple the caller from the callee. The caller doesn't care who the callee is; all it knows is that there is a callee with a certain prototype and probably some restriction (for instance, the returned value can be int, but certain values have certain meanings).

If you are wondering how is that useful in practice, imagine that you want to write a library that provides implementation for sorting algorithms (yes, that is pretty classic), such as bubble sort, shell short, shake sort, quick sort, and others. The catch is that you don't want to embed the sorting logic (which of two elements goes first in an array) into your functions, making your library more general to use. You want the client to be responsible to that kind of logic. Or, you want it to be used for various data types (ints, floats, strings, and so on). So, how do you do it? You use function pointers and make callbacks.

A callback can be used for notifications. For instance, you need to set a timer in your application. Each time the timer expires, your application must be notified. But, the implementer of the time'rs mechanism doesn't know anything about your application. It only wants a pointer to a function with a given prototype, and in using that pointer it makes a callback, notifying your application about the event that has occurred. Indeed, the SetTimer() WinAPI uses a callback function to notify that the timer has expired (and, in case there is no callback function provided, it posts a message to the application's queue).

Another example from WinAPI functions that use callback mechanism is EnumWindow(), which enumerates all the top-level windows on the screen. EnumWindow() iterates over the top-level windows, calling an application-provided function for each window, passing the handler of the window. If the callee returns a value, the iteration continues; otherwise, it stops. EnumWindows() just doesn't care where the callee is and what it does with the handler it passes over. It is only interested in the return value, because based on that it continues its execution or not.

However, callback functions are inherited from C. Thus, in C++, they should be only used for interfacing C code and existing callback interfaces. Except for these situations, you should use virtual methods or functors, not callback functions.

A Simple Implementation Example

Now, follow the example that can be found in the attached files. I have created a dynamic linked library called sort.dll. It exports a type called CompareFunction:

typedef int (__stdcall *CompareFunction)(const byte*, const byte*);

which will be the type of your callback functions. It also exports two methods, called Bubblesort() and Quicksort(), which have the same prototype but provide different behavior by implementing the sorting algorithms with the same name.
 

void DLLDIR __stdcall Bubblesort(byte* array,
                                 int size,
                                 int elem_size,
                                 CompareFunction cmpFunc);

void DLLDIR __stdcall Quicksort(byte* array,
                                int size,
                                int elem_size,
                                CompareFunction cmpFunc);


These two functions take the following parameters:

  • byte* array: a pointer to an array of elements (doesn't matter of which type)
  • int size: the number of elements in the array
  • int elem_size: the size, in bytes, of an element of the array
  • CompareFunction cmpFunc: a pointer to a callback function with the prototype listed above

The implementation of these two functions performs a sorting of the array. But, each time there is a need to decide which of two elements goes first, a callback is made to the function whose address was passed as an argument. For the library writer, it doesn't matter where that function is implemented, or how it is implemented. All that matters it is that it takes the address of two elements (that are the two be compared) and it returns one of the following values (this is a contract between the library developers and its clients):

  • -1: if the first element is lesser and/or should go before the second element (in a sorted array)
  • 0: if the two elements are equal and/or their relative position doesn't matter (each one can go before the other in a sorted array)
  • 1: if the first element is greater and/or should go after the second element (in a sorted array)

With this contract explicitly stated, the implementation of the Bubblesort() function is this (for Quicksort(), which a little bit more complicated, see the attached files).

void DLLDIR __stdcall Bubblesort(byte* array,
                                 int size,
                                 int elem_size,
                                 CompareFunction cmpFunc)
{
   for(int i=0; i < size; i++)
   {
      for(int j=0; j < size-1; j++)
      {
         // make the callback to the comparison function
         if(1 == (*cmpFunc)(array+j*elem_size,
                  array+(j+1)*elem_size))
         {
            // the two compared elements must be interchanged
            byte* temp = new byte[elem_size];
            memcpy(temp, array+j*elem_size, elem_size);
            memcpy(array+j*elem_size,
                   array+(j+1)*elem_size,
                   elem_size);
            memcpy(array+(j+1)*elem_size, temp, elem_size);
            delete [] temp;
         }
      }
   }
}
Note: Because the implementation uses memcpy(), these library functions should not be used for types other than POD (Plain-Old-Data).

 

On the client side, there must be a callback function whose address is to be passed to the Bubblesort() function. As a simple example, I have written a function that compares two integer values and one that compares two strings:

To put all these to a test, I have written this short program. It passes an array with five elements to Bubblesort() or Quicksort() along with the pointer to the callback functions.

int main(int argc, char* argv[])
{
   int i;
   int array[] = {5432, 4321, 3210, 2109, 1098};

   cout << "Before sorting ints with Bubblesort\n";
   for(i=0; i < 5; i++)
      cout << array[i] << '\n';

   Bubblesort((byte*)array, 5, sizeof(array[0]), &CompareInts);

   cout << "After the sorting\n";
   for(i=0; i < 5; i++)
      cout << array[i] << '\n';

   const char str[5][10] = {"estella",
                            "danielle",
                            "crissy",
                            "bo",
                            "angie"};

   cout << "Before sorting strings with Quicksort\n";
   for(i=0; i < 5; i++)
      cout << str[i] << '\n';

   Quicksort((byte*)str, 5, 10, &CompareStrings);

   cout << "After the sorting\n";
   for(i=0; i < 5; i++)
      cout << str[i] << '\n';

   return 0; 



If I decide that I want the sorting to be done descending (with the biggest element first), all I have to do is to change the callback function code, or provide another that implements the desired logic.

Calling Conventions

In the above code, you can see the word __stdcall in the function's prototype. Because it starts with a double underscore, it is, of course, a compiler-specific extension, more exactly a Microsoft-specific one. Any compiler that supports development of Win32-based applications must support this or an equivalent one. A function that is marked with __stdcall uses the standard calling convention so named because all Win32 API functions (except the few that take variable arguments) use it. Functions that follow the standard calling convention remove the parameters from the stack before they return to the caller. This is the standard convention for Pascal. But in C/C++, the calling convention is that the caller cleans up the stack instead of the called function. To enforce that a function uses the C/C++ calling convention, __cdeclmust be used. Variable argument functions use the C/C++ calling convention.

Windows adopted the standard calling convention (Pascal convention) because it reduces the size of the code. This was very important in the early days of Windows, when it ran on systems with 640 KB RAM.

If you don't like the word __stdcall, you can use the CALLBACK macro, defined in windef.h, as

#define CALLBACK    __stdcall

or

#define CALLBACK    PASCAL

where PASCAL is #defined as __stdcall.

You can read more about calling convention here: Calling Convetions in Microsoft Visual C++.

C++ Methods as Callback Functions

Because you probably write in C++, you want your callback function a method of a class. But, if you try this:

class CCallbackTester
{
public:
   int CALLBACK CompareInts(const byte* velem1, const byte* velem2);
};

Bubblesort((byte*)array, 5, sizeof(array[0]),
           &CCallbackTester::CompareInts);


with a MS compiler, you get this compilation error:

error C2664: 'Bubblesort' : cannot convert parameter 4 from 'int (__stdcall CCallbackTester::*)(const unsigned char *,const unsigned char *)' to 'int (__stdcall *)(const unsigned char *,const unsigned char *)' There is no context in which this conversion is possible

That happens because non-static member functions have an additional parameter, pointer this (see thisFAQ for more).

That obliges you to make the member function static. If that's not acceptable, you can use several techniques to overcome that. Check the following links to learn more.

Notices

The attached files contain two projects. SortingDLL is a Win32 DLL project. The sort.dll output library exports the two sorting functions, Bubblesort() and Quicksort(). The second project, SortDemo, is a Win32 Console Application that demonstrates how to use the sort.dll library. The output directory for both projects is Shared directory, where the following files can be found: sort.h, sort.dll, sort.lib, and SortDemo.exe.

Further References

  
 «이전 1  다음»