CPSC 427: Object-Oriented Programming
Michael J. Fischer
Function-Like Constructs
Named functions C and C++ support named global functions. Declaration syntax is
return_type name( parameters...) {function_body}
C++ also permits named member functions. These are declared inside of
a class using the same syntax as for named global functions.
The function name is qualified by the class name. Thus, the name of f within class C is C::f
Function calls
The syntax for calling a named global function is
name( arguments...)
The function call is an expression whose type is the return_type of the
function.
The syntax for calling a named member function of class C is
implicit_arg.name( explicit_arguments ) .
The implicit argument must be an instance of C. If called from within C, the implicit argument defaults to *this.
Function types
The type of a named global function is its signature, that is, the return
type and the list of parameter types.
For example, a function with a double and an integer reference as parameters that returns a long integer would have signature
long int (&) (double, int&)
To define such a function, we would add names for the function and parameters and a body to perform the computation.
long int (myfun) (double d, int& k) {
return (k += 2.5*d);
}
The parens around myfun are optional and would normally be omitted.
typedef for function types
Function types can be named using typedef. The declaration
typedef long int (myFunType) (double, int&);
defines the type name myFunType.
One can then declare a function to be of that type.
myFunType myfun;
and use it to define an alias myfun2 for myfun:
myFunType& myfun2 = myfun;
Without the typedef, it would look like this:
long int (&myfun2) (double, int&) = myfun;
(See demo 25a-Functors/funtypes.cpp.)
A new way to give names to types
C++ now provides another syntax for defining new type names (since
c++11). Instead of
typedef long int (myFunType) (double, int&);
one can write
using myFunType = long int (&) (double, int&);
(See demo 25a-Functors/using.cpp.)
Function pointers
One can have pointers to functions. Function points can be passed as
the argument to functions that take function parameters such
as the standard template function sort(). The third argument
is the comparison function comp to be used when compairing
elements. It returns true if its first argument is “smaller” than its
second.
bool descendingOrder( int i, int j)
{ return (j<i); }
using Comp = bool (*) (int, int);
Comp cptr;
cptr = descendingOrder;
sort (myvector.begin(), myvector.end(), cptr);
(See demo 25a-Functors/sort-funptr.cpp.)
Anonymous functions (a.k.a. lambda functions)
C++ now has anonymous functions. The previous example could be rewritten as
sort (myvector.begin(), myvector.end(),
[] (int i, int j) { return (j<i);} );
(See demo 25a-Functors/sort-lambda.cpp.)
Functors
A functor an instance of a class that defines operator(). It can be called using function syntax. Here’s how it can be used as a sort comparator.
(See demo 25a-Functors/sort-functor.cpp.)
Closures
Functors and lambda expressions gain their power through closures.
A closure is an expression in which some of the variables have been
bound to values, whereas other remain as function parameters.
A closure can be used like any other function.
A functor with data members acts like a closure. It result may depend on the values of those data members. Different instantiations of the functor class may result in functors with different behaviors since the data members may have different values.
Lambda capture
A lambda expression has the (simplified) syntax:
[capture_list] (parameters…) -> result_type {body}
When evaluated, the result is a closure with the variables on the capture
list being “imported” into the body.
In fact, when a lambda expression is evaluated, the result is a functor of
a new compiler-generated class.
The function template in header file <functional> can be used to turn a closure into an object of a function type.
Closure example
Demo 25a-Functor/closure uses map, function, and closures with
value-capture to define a table of sales tax functions.
The map key is a state name (or abbreviation), stored as a string. The
map value is a tax computation function that takes a sales amount as its
argument and returns the sales tax. It uses the captured variable rate to
computer the tax.
Each map pair has a tax computation function that was generated at run time to use a particular tax rate in its computation. The rate capture takes place in the line
Graphical User Interfaces
User Interfaces Modern computer systems support two primary general-purpose user interfaces: Command line: User input is via a command line typed at the keyboard. Output is character-based and goes to a physical or simulated typewriter-like terminal. Graphical User Interface (GUI): User input is via a pointing device (mouse), button clicks, and keyboard. Output is graphical and goes to a window on the screen.
Interfaces for C++
The C++ standard specifies a command line interface: iostream and associated packages. No similar standard exists for GUIs.
De facto GUI standards in the Linux world are GTK+ (used by the Gnome desktop) and Qt (used by the KDE desktop).
GTK+ is based on C; Qt is based on an extension of C++ and requires a special preprocessor.
gtkmm is a C++ wrapper on top of GTK+.
Advantages:
Provides
type
safety,
polymorphism,
and
subclassing.
Provides
a
native
interface
to
C++
code.
Disadvantages:
Components
not
so
well
integrated.
Documentation
spread
between
gtkmm,
gtk+,
and
other
components
but
improving.
Overall Structure of a GUI
A GUI manages one or more windows.
Each window displays one or more widgets.
These are objects that provide graphical and textual input and output to
the program.
A GUI package such as gtkmm maintains a widget tree.
A widget controls a particular kind of user input or output.
Examples: label, text box, drawing area, button, scroll bar, etc.
Concurrency and Events
The central problem in building a GUI is handling concurrency.
Data arrives from multiple concurrent sources – mouse and keyboard,
network, disk, other threads, etc.
We call the arrival of a piece of data an event.
For example, to have a good interactive feel, the GUI should respond to a mouse click event within milliseconds.
Event Loop
An event loop allows a single thread to manage a set of events.
At some level, the hardware or software polls for events.
When an event is detected, it is dispatched to an event handler.
The event handler either processes the event itself, queues a task for
later processing, or spawns a thread to process it.
While the event thread is processing one event, no other events can be
processed, so event handlers should be short.
Problem is to prevent a long-running low-priority event handler from delaying the handling of a high-priority event.
A GUI event structure
A GUI typically translates raw hardware events into semantically-meaningful
software events.
For example, a mouse click at particular screen coordinates might turn
into a button-pressed event for some widget in the GUI tree.
Several system layers may be involved in this translation, from the kernel processing of hardware interrupts at the bottom level, up through device drivers, windowing systems such as X, and finally a GUI frameworks such as GTK+.
Interface between user and system code
A major software challenge is how to design the interface between the
GUI and the user code that ultimately deals with the events.
In a command-line interface, the user code is at the top level.
It connects to the lower layers through familiar function calls.
With a GUI, things are turned upside down.
Binding system calls to user functions
How can one write the GUI to call user functions that did not even exist
when the GUI system itself was written?
The basic idea is that of interface.
Polymorphic binding
C++ virtual functions provide an elegant way to bind user code to an interface.
Of course, the actual binding occurs at run time through the use of type tags and the vtable as we have seen before.
Binding through callback registration
The user explicitly registers an event handler with the GUI by calling a special registration function.
This is sometimes called a callback mechanism since the user asks to be called back when an event occurs.
Callback using function pointers: GUI side
Callbacks can be done directly in C. Here’s the GUI code:
Callback using function pointer: User side
Here’s how the user attaches a handler to the GUI:
Type safety
The above scheme does not generalize well to multiple events with different signatures.
The alternative is for systemRegister() to take a void* for
its second argument and to cast function pointers before call
them.
This is not type safe and can lead to subtle bugs if the wrong type of function is attached to a callback slot.
Signals and slots
Signals and slots is a more abstract way of linking events to handlers and can be implemented in a type safe way.
Several signals can be connected to the same slot, and several slots can be connected to the same signal.
The gtkmm Framework
Structure of gtkmm gtkmm relies on several libraries and packages:
Compiling a gtkmm program
Many include files and libraries are needed to compile and build a gtkmm program.
A utility pkg-config is used to generate the necessary command line for the compiler.
> pkg-config gtkmm-3.0 --cflags
Linking a gtkmm program
> pkg-config gtkmm-3.0 --libs
generates the necessary linker flags.
To use package config, use the backquote operator on the g++ command line:
Compiling: g++ -c ‘pkg-config gtkmm-3.0 --cflags‘ ...
Linking: g++ ‘pkg-config gtkmm-3.0 --libs‘ ...
Both: g++ ‘pkg-config gtkmm-3.0 --cflags --libs‘ ...
Using a GUI
The following steps are involved in creating a GUI using gtkmm:
The GUI then displays the window and waits for events.
When an event occurs, the corresponding user code is run.
When the event handler returns, the GUI waits for the next event.
Example: clock
The code example 25-clock is a significant extension of the clock
example in the gtkmm tutorial book.
It illustrates many of the features of gtkmm.
Main program