Closure come tipi di ritorno (C++0x)…

Nel post precedente ho presentato le lambda function/expression introdotte da C++0x, specificando che di fatto si tratta di funzioni-oggetto.

Proseguiamo nel discorso, cercando di mostrare almeno un caso in cui è comodo impiegarle…

Lamba e Polymorphic Function Wrapper

In questo piccolo codice di esempio ho creato una piccola funzione, nella quale viene definita una lambda.

Tale “funzione” non fa altro che sommare due interi e moltiplicarli per un valore randomico, creato da uno dei nuovi generatori di numeri random introdotti dallo standard (extensible random number facility), precisamente un Mersenne twister engine.

La lambda cattura “by reference” il generatore di numeri casuali definito anch’esso nella funzione createClosure(): si tratta quindi di una closure a tutti gli effetti.

La parte piacevole è però il poterla impacchettare (“wrap”) in polymorphic function wrapper (anch’essi un tipo di funzione-oggetto di cui ho già parlato in passato) e quindi ritornarla.

La nuova std::bind

Da notare anche l’utilizzo di std::bind (ripreso dal C++ TR1), che serve a “legare” più parametri ad una funzione o un oggetto.

In questo caso, legando l’“engine Mersenne twister” (parametro) all’oggetto “distribuzione uniforme di interi compresi fra 0 e 99“, si ottiene l’oggetto “generatore di numeri casuali“.

Molto elegante, non trovate?

Propriamente (sempre dall’ultimo draft pubblicato):

“The function template bind <returns an object that binds a function callable object passed as an argument to additional arguments”

L’introduzione di bind, definito come variadic template, rende quindi deprecate le “soluzioni” precedenti:

D.9 Binders
The binders binder1st, bind1st, binder2nd, and bind2nd are deprecated. [ Note: The function template bind (20.8.11) provides a better solution.—end note ]“

Vediamo dunque un piccolo codice di esempio che mostra questa possibilità (testato anche in questo caso con G++ 4.5 e con Visual C++ 2010)…

Codice di esempio

Nota: nonostante i commenti e l’estrema compattezza del codice, il seguente esempio non è esattamente semplice. Per cui non vi preoccupate se non riuscite a comprenderlo. Consideratelo, se volete, una sorta di una “demo” di alcune funzionalità del nuovo standard C++0x. Grazie.

/**
 *	FILE      : ReturnClosureExample.cpp
 *	AUTHOR    : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
 *	LICENSE   : released under the terms of GPL v2.0 ("only")
 *	COMPILE   : g++ -Wall -Winline -pedantic -std=c++0x
 *	            ReturnClosureExample.cpp -o ReturnClosureExample
 *	PURPOSE   : simple C++0x example showing lambda <-> polymorphic function wrapper usage
 *	            and how to return a closure
 *
 *	TESTED ON :
 *	- Windows XP SP3, x86 32-bit, G++ 4.5.0
 *	- Windows XP SP3, x86 32-bit, Visual C++ 2010
 *
 *	REFERENCES:
 *	[1]: http://en.wikipedia.org/wiki/Lambda_expression
 *	[2]: http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions
 *	[3]: http://en.wikipedia.org/wiki/Closure_%28computer_science%29
 *	[4]: http://gcc.gnu.org/gcc-4.5/cxx0x_status.html
 *	[5]: http://en.wikipedia.org/wiki/C%2B%2B0x#Alternative_function_syntax
 *	[6]: http://en.wikipedia.org/wiki/C%2B%2B0x#Polymorphous_wrappers_for_function_objects
 *	[7]: http://en.wikipedia.org/wiki/C%2B%2B0x#Extensible_random_number_facility
 *	[8]: http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference
 *	[9]: http://en.wikipedia.org/wiki/C%2B%2B_Technical_Report_1#Function_object_binders
 */

#include <cstdlib>
#include <iostream>
#include <functional> // for [6]
#include <random>     // for [7]

using namespace std;

// this function will return a "polymorphic function wrapper" [6]
inline function<int(int, int)> returnClosure()
{
	uniform_int_distribution<int> distribution(0, 99); // uniform integer distribution
	mt19937 engine;                                    // Mersenne twister engine
	auto generator = bind(distribution, engine);       // binding the objects [8, 9]

	// returning a closure: the lambda will be automatically
	// "converted" in a "polymorphic function wrapper" [6]!
	return
	(
		// the lambda-closure captures the "generator" object by-reference.
		// moreover it uses the alternative function syntax ("-> int") to explicitly
		// specify the return type [5]
    	[&generator](int a, int b) -> int
    	{
			int rndNumber = generator(); // obtaining a new random value
			return (b - a) * rndNumber;
    	}
	);
}

int main(int argc, char ** argv)
{
	cout << "\nC++0x EXAMPLE:" << endl;
	cout << "Simple example showing how to return closures in C++0x\n" << endl;

	// storing the returned closure in/as a "polymorphic function wrapper" [6]
	function<int(int, int)> pfWrapper = returnClosure(); // [1, 2, 3, 4, 6]

	for(int i = 0; i < 5; i++) // each time we call pfWrapper, rndNumber changes...
		cout << "Return value: " << pfWrapper(5, 11) << endl; // prints '6 * rndNumber'

    return EXIT_SUCCESS;
}

Il codice in esecuzione

C++0x example: returning closures

P.S.: spero che Contezero apprezzi lo sforzo (mio) e l’eleganza (delle lambda)… :P

Contrassegnato da tag , , , , , ,

3 thoughts on “Closure come tipi di ritorno (C++0x)…

  1. Martino scrive:

    Saluti Paolo,

    bell’articolo (come sempre!!!) L’esempio di per se non è troppo complesso se si hanno abbastanza chiari i due concetti principali dell’esempio stesso: le lambda function e il function wrapper. Personalmente avrei “typedeffato” function e magari usato la keyword ‘auto’ nel main ma sono ‘gusti’…

    Una cosa non mi è chiara. generator viene passato come ref alla funzione lambda; teoricamente, forse qui che sbaglio, quando la funzione termina la variabile esce dallo scope e viene eliminata dallo stack. In realtà però viene riutilizzata nell’invocazione della funzione lambda successivamente. C’è qualche cosa che mi sfugge, il catturare la variabile implica il passaggio della ownership?…

    molte grazie come sempre

  2. Martino scrive:

    ps aggiungo che sono molto felice che sono state deprecate quelle brutte e poco usabili funzioni di binding…avevano troppe limitazioni!

  3. jp scrive:

    @martino: diciamo che ritorno qualcosa (come se fosse) “per copia”, per cui “the problem is not quite as severe as it seems” (cfr. post).

    Comunque ragionando per via teorica, hai perfettamente ragione: volendo potevo/potrei incapsulare tutto in uno shared_ptr(), ma renderebbe tutto più difficile da leggere… :P

    Per chiudere il cerchio, alla fine della fiera l’oggetto-ancora è pfWrapper, che rimane nello scope della main() fino alla fine.

    Esso “riceve” la closure – all’uscita della funzione che la produce – (un prvalue temporaneo chiamato “closure type”=> han cambiato ancora la nomenclatura, argh!) che è anch’essa a tutti gli effetti una funzione-oggetto con le sue belle variabili “importate” dal contesto in cui era stata definita (cioè le variabili catturate).

    Rimane la questione relativa a “scope e vita (prolungata) della lambda/closure stessa“: ho riletto svariate volte tutta la parte del draft riguardante questo argomento e, da quanto ho liberamente interpretato, una “lambda” può sopravvivere finchè anche l’ultimo contesto che la usa termina (tipo “l’ultimo chiude la porta”, in questo caso la main() stessa).

    Dal draft:

    “A lambda-expression whose smallest enclosing scope is a block scope (3.3.3) is a local lambda expression; any other lambda-expression shall not have a capture-list in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters. [ Note: this reaching scope includes any intervening lambda-expressions. —end note ]“

    Credo che ciò implichi il continuare a mantenere in vita anche lo scope in cui è stata definita (per le variabili catturate!) finchè serve o, come spero, semplicemente “simulare” in qualche modo questo fatto. Il come credo sia al solito platform-dependent

    Ciau e grazie per essere passato! ^^

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Log Out / Modifica )

Foto Twitter

You are commenting using your Twitter account. Log Out / Modifica )

Foto di Facebook

You are commenting using your Facebook account. Log Out / Modifica )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 259 other followers