Home > C++ (C Plus Plus), LinkedIn, Programmazione > Esempio di alternate function syntax (C++0x)…

Esempio di alternate function syntax (C++0x)…

Salve a tutti.

Riprendiamo il discorso sul nuovo standard del C++, C++0x: in questa puntata parleremo di una nuova sintassi per le funzioni.

Con l’avvento del nuovo standard C++, C++0x, sono stati aggiunti due nuovi operatori, auto e decltype, che consentono ad esempio di determinare in maniera automatica i tipi dei parametri o dei valori di ritorno.

Dopotutto il compilatore non è stupido e può tranquillamente fare da sè/al posto nostro.

Questa feature è detta type inference e nella pratica permette di risparmiare un bel po’ di caratteri.

Un esempio tanto “abituale” quanto noioso (per comodità preso da Wikipedia):


// Esempio: supponiamo di avere un vettore di interi
//          chiamato myvec su cui vogliamo iterare

// 1) senza la type inference (C++ "attuale"):
//    decisamente verboso e noioso
for (vector<int>::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr) { ... }

// 2) con la type inference del C++0x (<em>operatore auto</em>):
//    più agevole e compatto
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr) { ... }

Penso che il vantaggio lato programmatore sia lampante ma l’efficacia di questa nuova feature emerge imperiosa in quelle situazioni in cui ad esempio non è facile determinare il tipo di ritorno di una funzione.

La type inference ha permesso inoltre l’introduzione di una nuova sintassi per le funzioni che consente di superare in maniera elegante un problema noto da tempo.

Procediamo con ordine…

La sintassi standard delle funzioni, ereditata dal C appare come:

TIPO_RIS somma(TIPO_PARAM_1 p1, TIPO_PARAM_2 p2) { ... }

Questa sintassi ha un problema: TIPO_RIS appare prima dei tipi dei parametri.

Quindi è impossibile fare in modo che ad esempio TIPO_RIS=TIPO_PARAM_1 o che TIPO_RIS=TIPO_PARAM_2 perchè al momento in il compilatore legge TIPO_RIS, TIPO_PARAM_1 e TIPO_PARAM_2 non sono ancora noti.

In altri termini con questa sintassi, se abbiamo una funzione template che somma due numeri e le passiamo un int ed un long come parametri, non possiamo fare in modo che il tipo di ritorno sia automaticamente impostato a long nonostante la type inference.

La nuova sintassi delle funzioni del C++0x, che si basa sulla type inference, consente questa magia con questa forma:

auto somma(TIPO_PARAM_1 p1, TIPO_PARAM_2 p2) -> decltype(p1+p2) { ... }

Stiamo dicendo che vogliamo che la funzione restituisca (->) un tipo che basti a contenere la somma degli addendi, ossia p1+p2.

Il “calcolo” del tipo in questo caso avviene tramite l’operatore decltype, ma se conosciamo e vogliamo impostare uno specifico tipo di ritorno, possiamo scriverlo esplicitamente senza usare quell’operatore (es: auto somma(TIPO_PARAM_1 p1, TIPO_PARAM_2 p2) -> long).

Comodo non trovate?

L’esempio che vi presento di seguito riassume quanto appena detto.

Spero che vi piaccia. :)

/**
 *	FILE      : AutoDecltypeExample.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
 *	            AutoDecltypeExample.cpp -o AutoDecltypeExample
 *	PURPOSE   : simple C++0x example showing C++0x type inference [1],
 *              alternate function syntax [2] and rvalue parameters [3]
 *	            in action
 *
 *	TESTED ON :
 *	- Windows XP, x86 32-bit, G++ 4.4.0
 *
 *	REFERENCES:
 *	[1]: http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference
 *	[2]: http://en.wikipedia.org/wiki/C%2B%2B0x#Alternate_function_syntax
 *	[3]: http://en.wikipedia.org/wiki/C%2B%2B0x#Rvalue_reference_and_move_semantics
 *	[4]: http://en.wikipedia.org/wiki/C%2B%2B0x#Type_long_long_int
 *	[5]: http://en.wikipedia.org/wiki/RTTI
 */

#ifdef __STRICT_ANSI__ // to avoid a MinGW G++ 4.4.0 bug ("swprintf undeclared...")
	#undef __STRICT_ANSI__
	#include <cstdlib>
	#include <iostream>
	#define __STRICT_ANSI__
#else
	#include <cstdlib>
	#include <iostream>
#endif

#include <cmath>
#include <typeinfo>

using namespace std;

/*
   Template function including some C++0x new features: type inference [1],
   alternate function syntax [2] and rvalue parameters [3]

   In this case the alternate function syntax (auto ... ->) is used
   to explicitly determine the return type, that will the type with the biggest size
   between the two addends. The computation is performed through decltype operator.

 */
template<typename A, typename B>
auto sum(const A &&a, const B &&b) -> decltype(sizeof(a) > sizeof(b) ? a : b)
{
	return a + b;
}

int main(int argc, char **argv)
{
	cout << "C++0x EXAMPLE:" << endl;
	cout << "Simple example showing some C++0x features" << endl << endl;

	// - "long long" type is now part of C++ [4]
	// - we use Run-time type information (RTTI) to verify
	//   the type of the results [5];

	auto r1 = sum<double, int>(2, 5);
	cout << "- r1 type: '" << typeid(r1).name() << "'" << endl; // r1 type name = "d" (double)

	auto r2 = sum<int, long>(2, 5);
	cout << "- r2 type: '" << typeid(r2).name() << "'" << endl; // r2 type name = "l" (long)

	auto r3 = sum<long long, long>(2, 5);
	cout << "- r3 type: '" << typeid(r3).name() << "'" << endl; // r3 type name = "x" (long long)

	return EXIT_SUCCESS;
}

  1. cavok
    12 Giugno 2009 alle 09:25 | #1

    non vedo l’ora che arrivi auto (no, no Automan…).

    un altro caso in cui mi piacerebbe usarlo e’ questo, scrivere B invece che A. ci credero’ solo quando lo vedro’.

    A) boost::recursive_mutex::scoped_lock lock(monitor);
    B) auto lock(monitor);

  2. 12 Giugno 2009 alle 09:51 | #2

    Bello !
    E’ da più di un anno che non programmo in C++, e devo dire che non mi manca. Ma queste novità mi fanno tornare la voglia di scrivere qualche riga di (bel) codice ..

  3. jp
    12 Giugno 2009 alle 10:47 | #3

    @cavok: ho provato. Al momento non funziona e non ho modo di sapere se è un problema del G++ 4.4.0 o di qualche limitazione particolare della type inference stessa.

    Probabilmente la type inference è limitata in presenza di tipi non primitivi/non-POD (c’è una modifica anche alla definizione di POD e non capisco se il G++ la include già o no) come quelli impiegati dalle Boost, dal fatto che ci siano più scelte possibili in quel contesto, oppure il supporto nel G++ non è ancora completo al 100%…

    // g++ -IC:\Programmi\boost\boost_1_38\ -O0 -g3 -Wall
    //      -c -fmessage-length=0 -std=c++0x TestBoost.cpp -o TestBoost
    
    #include <boost/thread/recursive_mutex.hpp>
    
    int main(int argc, char **argv)
    {
    	boost::recursive_mutex monitor;
    	boost::recursive_mutex::scoped_lock lock(monitor); // ok
    	auto lock2(monitor); // ERRORE! "error: 'lock2' has incomplete type"!
    
    	return 0;
    }
    

    Quando G++ e stdc++ supporteranno i thread (feature ufficiale di C++0x), verificherò se la type inference funziona con le relative classi “standard”… :)

    @stefano: diciamo che c’è un bel po’ di carne sul fuoco… con cui scottarsi volontariamente… :D

  4. cavok
    13 Giugno 2009 alle 16:38 | #4

    @jp: eh.. figurati.. :/

  5. jp
    14 Giugno 2009 alle 22:53 | #5

    Beh, come detto, le cose son così al momento. Speriamo in bene per il futuro… ^^

  1. No trackbacks yet.