Application pratique du C++ pour mes étudiants

Pour mes étudiants en 4ème année, j’ai pris comme modèle d’application 2 styles d’app:

  • Serveur Web REST API avec Microsoft CPPREST SDK
  • Une application Windows graphique MFC

modeler2_shapes_infra

Je leur ai expliqué qu’il faut des couches d’abstractions entre la plomberie de tel ou tel SDK et le code C++ métier.

Pour le serveur Web REST API en moins de 200 lignes de code, c’est bluffant. Pour faire le back-end d’une application mobile, c’est le candidat parfait.

J’ai senti qu’ils étaient très intéressés de voir que C++, c’est concret.

La Programmation Orientée Objet en C++

Introduction

Il y a plusieurs façons d’utiliser le C++. On peut être utilisateur de classes ou concepteur de classes. Dans les deux cas, définir, designer et implémenter des classes sont les activités primaires des développeurs C++. Par où on commence ? En général, on démarre avec une abstraction ou un concept. On considère un truc à coder et puis on essaie de voir comment on va gérer son ou ses fonctions, ce qui est interne et ce qui est public et comment on va l’utiliser… Au démarrage de cette réflexion, il faut se mettre la casquette de celui qui va utiliser la classe. Notre exemple de concept pour cet article sera un élément graphique à dessiner comme une ligne, un rectangle, une ellipse, etc. En anglais, c’est un shape.

// C'est une déclaration de classe
class CShape;

// C'est la future manière avec laquelle je veux dessiner des Shapes
bool Draw(const CShape& item);

Ensuite on envisage la classe dans son ensemble :
class CShape
{
public:
	// interface publique

private:
	// implémentation privée
};

Qu’est-ce que l’on met en private/public et comment va-t-elle être structurée.  On commence petit bras et puis la classe prend du volume.

Constructeur et Destructeur

La première question qui vient à l’esprit est comment je vais créer mon objet et ai-je besoin de lui fournir des paramètres. Avec ma classe CShape, on peut se dire, je n’ai pas besoin mais aussi j’en ai besoin. Les deux possibilités existent ! Soit c’est une figure connue -> d’où une énumération pour les types connus et autre possibilité qui est de dessiner une image et là, il me faut un nom de fichier ; tout est ouvert !

enum ShapeType
{
	line,
	circle,
	rectangle,
	left_arrow,
	right_arrow,
	picture
};

L’avantage de l’énumération es que c’est évalué à la compilation. Il ne peut pas y avoir d’erreur sur le nommage car c’est un type explicite contrairement à une simple chaîne de caractères. Voyons sommairement comment pourrait être construite cette fameuse classe CShape…

class CShape
{
public:
	CShape();
	CShape(int x, int y, int xx, int yy, ShapeType type);
	CShape(int x, int y, int xx, int yy, std::string fileName);
	virtual ~CShape();

private:
	std::string m_fileName;
	ShapeType m_type;
};

J’y vois deux façons de créer un shape. Soit à partir de l’énumération soit à partir d’un fichier pour les images. Pour le moment, je ne sais pas comment organiser le dessin du shape. Dois-je le mettre à l’intérieur de la classe ou à l’extérieur ? Je ne sais pas… Il faut du temps pour considérer quelle sera la façon la plus naturelle de réaliser cette opération. Dans le monde Windows GDI+, pour dessiner un élément il faut un handle particulier qui possède toutes les primitives de dessins.  En pensant comment allait être dessiner un élément, on peut envisager d’inclure une méthode Draw dans la classe CShape. La première pensée, c’est de se dire, je vais implémenter tous les cas possibles dans Draw avec une construction qui ressemble à ça :

	void Draw(const CDrawingContext& ctxt) const
	{
		if (m_type == ShapeType::line)
		{
			ctxt.Line(m_rect.left, m_rect.bottom,
				m_rect.top, m_rect.right);
		}
		if (m_type == ShapeType::rectangle)
		{
			ctxt.Rectangle(m_rect);
		}
		// TODO
	}

Dans la classe, il est important de marquer les méthodes qui ne modifient pas les données private. Pour faire cela, on leur ajoute le mot-clé const à la fin comme c’est indiqué sur la méthode Draw.

De plus, lorsque l’on passe un argument qui n’as pas vocation à être modifié, on le passe en référence const. Comme on peut le voir ci-dessus, la définition d’une classe est un travail itératif dans lequel, les meilleures idées chassent celles d’avant ou les conforte. Si une classe est bien conçue, le lecture de ses membres public est limpide car on va à l’essentiel. Attention, les membres private expliquent aussi beaucoup de choses sur les détails d’implémentation. Maintenant, mettons-nous dans un contexte où il existe plusieurs classes et les principes vu ci-dessus ne suffisent pas à faire un modèle objet. Il nous faut des mécanismes plus sophistiqués pour relier les classes les unes avec les autres.

Les classes possèdent des associations et des comportements et c’est ici que les Jedi se positionnent pour y designer un modèle élégant, facile à utiliser et qui rend les services voulus.

Notre méthode Draw n’est pas OOP car si je rajoute un type dans l’énumération, je dois ajouter un if() dans la méthode Draw() et coder l’implémentation. On peut faire mieux, beaucoup mieux.

Les concepts de OOP

Les deux piliers de l’OOP sont l’héritage et le polymorphisme. L’héritage permet de grouper les classes en famille de types et permet de partager des opérations et des comportements et des données. Le polymorphisme permet de déclencher des opérations dans ces familles à un niveau unitaire. Ainsi ajouter ou supprimer une classe n’est pas trop un problème.

L’héritage définit une relation parent/enfant. Le parent définit l’interface public et l’implémentation private est commune à tous ses enfants. Chaque enfant choisit s’il veut hériter d’un comportement unique ou bien le surcharger. En C++, le parent est appelé « classe de base » et l’enfant « classe dérivée ». Le parent et les enfants définissent une hiérarchie de classes. Le parent est souvent une classe abstraite et les enfants implémentent les méthodes virtuelles pures pour que l’ensemble fonctionne.

Dans un programme OOP, on manipule les classes via un pointeur sur la classe de base plutôt que sur les objets dérivés.

Une classe abstraite

Revenons sur la classe CShape… La prochaine étape dans le design est de créer une classe abstraite pour identifier les opérations propres à chaque item – ce qui implique des opérations avec une implémentation basée sur une classe dérivée. Ces opérations sont des fonctions virtuelles pures de la classe de base. Une méthode virtuelle pure est une méthode abstraite. Il n’y a pas de corps. Cela implique que le classe devient abstraite aussi. Il n’est pas possible de créer un objet à partir d’une classe abstraite. La méthode Draw() doit être définie comme virtuelle pure car il y a plusieurs types de dessin à faire et que chaque dessin est propre à une classe donnée. Le corps de Draw() n’existe pas, c’est la raison pour laquelle on dit que la classe est abstraite ; il y a au moins une méthode virtuelle pure.

Voici donc comment on fait :

class CShapeEx
{
public:
	CShapeEx() {}
	CShapeEx(ShapeType type, RECT rect, const std::string &fileName) 
		: m_type(type), m_rect(rect), m_fileName(fileName) {}
	virtual ~CShapeEx() {}

public:
	virtual void Draw(const CDrawingContext& ctxt) const = 0;
	virtual void DrawTracker(const CDrawingContext& ctxt) const = 0;

private:
	std::string m_fileName;
	ShapeType m_type;
	RECT m_rect;
};

Et nous allons maintenant déclarer des classes dérivées de CShapeEx :

class CRectangle : public CShapeEx
{
public:
	CRectangle() {}
	virtual ~CRectangle() {}

public:
	virtual void Draw(const CDrawingContext& ctxt) const
	{
		// TODO
		std::cout << "Rectangle::Draw" << std::endl;
	}

	virtual void DrawTracker(const CDrawingContext& ctxt) const
	{
		// TODO
	}
};

class CLine : public CShapeEx
{
public:
	CLine() {}
	virtual ~CLine() {}

public:
	virtual void Draw(const CDrawingContext& ctxt) const
	{
		// TODO
		std::cout << "Line	::Draw" << std::endl;
	}

	virtual void DrawTracker(const CDrawingContext& ctxt) const
	{
		// TODO
	}
};

Si on veut rajouter un item à dessiner dans la hiérarchie, il suffit de rajouter une classe dérivée ! Maintenant nous allons voir comment fonctionne ce mécanisme de virtual…

	CDrawingContext ctxt;
	CRectangle * pRect = new CRectangle();
	CLine * pLine = new CLine();
	CShapeEx * ptr = nullptr;
	ptr = pRect;
	ptr->Draw(ctxt);
	ptr = pLine;
	ptr->Draw(ctxt);

On commence par déclarer 2 items à dessiner. Puis le manager, un pointeur sur la classe de base CShapeEx est déclaré. A chaque fois qu’il pointe sur un objet d’une classe dérivée, la fonction virtuelle Draw() appelée est celle de la classe dérivée car cette fonction est définie comme virtuelle.

code5

Maintenant, il nous manque quelque chose de très utile et qui évitera les usines à gaz de construction d’objets dérivés. Il nous faut une factory ! Un mécanisme de création d’objet.

La factory

class CFactory
{
private:
	CFactory();

public:
	static CShapeEx * CreateObject(ShapeType type)
	{
		if (type == ShapeType::line)
		{
			CLine * pObj = new CLine();
			pObj->m_type = type;
			return pObj;
		}

		if (type == ShapeType::rectange)
		{
			CRectangle * pObj = new CRectangle();
			pObj->m_type = type;
			return pObj;
		}

		// TODO

		return nullptr;
	}
};

Cette classe permet via une méthode static de créer un objet en fonction de son type dans l’énumération. Le type est affecté en variable membre de l’objet créé. On notera que le constructeur est private, cela veut dire que l’on ne peut pas déclarer d’objet CFactory. On ne peut qu’utiliser la méthode static CreateObject. Sauf que, la compilation tombe en erreur :

code6

Le membre m_type est inaccessible vue son niveau de protection… On va donc ajouter quelque chose à la classe CShapeEx :

class CShapeEx
{
public:
	CShapeEx() {}
	CShapeEx(ShapeType type, RECT rect, const std::string &fileName) 
		: m_type(type), m_rect(rect), m_fileName(fileName) {}
	virtual ~CShapeEx() {}

public:
	virtual void Draw(const CDrawingContext& ctxt) const = 0;
	virtual void DrawTracker(const CDrawingContext& ctxt) const = 0;

private:
	std::string m_fileName;
	ShapeType m_type;
	RECT m_rect;

	// La classe CFactory peux avoir accès aux membres...
	friend class CFactory;
};

On ajoute en fin de classe que la classe CFactory est amie de la classe CShapeEx. Et là, il n’y a plus d’erreur de compilation ! Les puristes comme AlainZ, à Redmond, vous diront que cela viole la mécanique objet et que c’est une construction exotique et qu’il vaut mieux passer par le constructeur pour passer le type… Je ne suis pas de cet avis. Je pense que c’est élégant de faire une entorse pour la factory. Voici maintenant le code qui appelle la factory :

	CDrawingContext ctxt;
	CShapeEx * pRect = CFactory::CreateObject(ShapeType::rectange);
	CShapeEx * pLine = CFactory::CreateObject(ShapeType::line);
	CShapeEx * ptr = nullptr;
	ptr = pRect;
	ptr->Draw(ctxt);
	ptr = pLine;
	ptr->Draw(ctxt);

Et le résultat est équivalent.

Compilation sous Linux

Pour ceux qui ne le savent pas, le langage C++ est normalisé ISO. Sur la plateforme Linux, là où tout est fait en C/C++, le langage possède plusieurs compilateurs dont le célèbre GCC. Vous pouvez utiliser Visual Studio Code que fournit Microsoft comme IDE pour compiler sous Linux via le terminal.

Il faut installer l’extension C++. Moi j’ai installé Cygwin et MSYS. Ainsi je dispose de commandes Linux.

code7

Une touche de C++ 11

On va essayer de continuer notre application de dessin en matérialisant la zone de dessiner. Cela ressemble à un ensemble de shapes sur un support de dessin. Première étape quand on pense au concept de la planche à dessin c’est quoi ? Le stockage des éléments graphiques dans un container STL. Oui Monsieur, quand on veut stocker quelque chose, on passe par les containers STL. On va donc utiliser le std::vector de la STL. On va stocker des shapes un peu particulier que sont les shapes partagés. Partagés ? Par qui ? Dans un vrai logiciel de dessin, on affiche les propriétés dans une grille et le dessin est sur le panneau central donc on partage nos objets et ce n’est pas que de l’affichage. Je vous montre le résultat.

code8

Sur cet écran, on voit que le rectangle gris sélectionné a ses attributs directement dans le grille de propriétés mais aussi sur le Ribbon. Comment faire pour partager les données entre le panneau de dessin, le Ribbon et la grille de propriétés ? Est-ce que l’on fait des copies des objets ? Oui, mais on fait des copies de pointeurs et non des copies d’objets. Pour faire cela, une utilise le mécanisme des pointeurs partagés de la STL. Voyons son utilisation. Premièrement, on va designer la zone de dessin.

La zone de dessin

Cette zone contient les objets shape à dessiner. On va donc créer un vector. Le container vector est défini dans . On va avoir une collection d’éléments partagés CShapeEx. Ainsi, on pourra stocker ce pointeur particulier où on veut sans se soucier de la libération de la mémoire. La zone de dessin est une vue fille MDI en jargon Windows. Elle possède deux scrollbars, un verticale et une horizontale. Si on veut faire une gestion de shared_ptr, il faut faire évoluer la factory :

	static std::shared_ptr CreateObject(ShapeType type)
	{
		std::shared_ptr pObj = nullptr;

		if (type == ShapeType::line)
		{
			pObj = std::make_shared();
			pObj->m_type = type;
			return pObj;
		}

		if (type == ShapeType::rectangle)
		{
			pObj = std::make_shared();
			pObj->m_type = type;
			return pObj;
		}

		return nullptr;
	}

On ne fait plus un new() mais un appel à std::make_shared().

De plus, dans l’utilisation des objets, cela donne ça :

	CDrawingContext ctxt;
	std::shared_ptr pRect = CFactory::CreateObject(ShapeType::rectangle);
	std::shared_ptr pLine = CFactory::CreateObject(ShapeType::line);
	std::shared_ptr ptr = nullptr;
	ptr = pRect;
	ptr->Draw(ctxt);
	ptr = pLine;
	ptr->Draw(ctxt);

Il existe même une façon de laisser le compilateur déterminer le type de variable utilisée en utilisant auto :

	CDrawingContext ctxt;
	auto pRect = CFactory::CreateObject(ShapeType::rectangle);
	auto pLine = CFactory::CreateObject(ShapeType::line);
	auto ptr = pRect;
	ptr->Draw(ctxt);
	ptr = pLine;
	ptr->Draw(ctxt);

Revenons à notre design de classes. Le container de shapes est juste une collection std::vector de std::shared_ptr :

class CElementContainer : public CObject
{
private:
	
public:
	DECLARE_SERIAL(CElementContainer);
	CElementContainer();
	virtual ~CElementContainer(void);

// ../..
// overridden for document i/o
virtual void Serialize(CElementManager * pElementManager, CArchive& ar);   
// Debuging Operations
public:
	void DebugDumpObjects(CModeler1View * pView);

// Operations MFC like
public:
	std::shared_ptr GetHead();
	int GetCount();
	void RemoveAll();
	void Remove(std::shared_ptr pElement);
	void AddTail(std::shared_ptr pElement);

// Attributes
private:
	vector m_objects;
};

Là, je vous montre un vrai code et les éléments sont matérialisés par la classe CElement mais c’est pareil que CShapeEx. On notera que les fonctions de gestion au vector sont des méthodes helpers dans cette classe. On notera aussi que la fonction Serialize qui permet de faire Load/Save est gérée dans cette classe. En effet, si il existe des donnés à mettre dans un fichier de données, c’est bien cette classe. Il faut maintenant intégrer cette classe dans la mécanique de fenêtrage Windows. Dans un canevas MFC, on possède un Document pour y stocker les données et une View pour y faire les opérations d’affichage.

Abstraction

La couche de dessin est confiée à une classe explicite et les méthodes ne sont pas directement insérées dans la vue. Pourquoi un tel niveau de poupées russes ? Parce que sinon, c’est crado. C’est trop entrelacé dans les couches Windows et le code est spaguetti. J’ai donc un e classe qui se nomme CElementManager qui fait le lien avec la View MFC. De ce fait beaucoup de méthodes prennent en paramètre une View… C’est de la délégation de comportement. Mais dans cette classe on trouve tous les éléments de la zone de dessin :

  • Les objets à afficher
  • Les éléments d’une sélection
  • Les éléments du presse-papier
  • La couleur de fond de la zone
  • Le mode du pointeur de souris
  • Le facteur de zoom
  • Un booléen pour savoir si on est en train de dessiner un gabarit
  • L’élément courant avec son type et son id et son nom

Voici un aperçu du fichier d’entêtes de la classe CElementManager :

code9

Cette classe est juste dépendante d’une View sur laquelle il va y avoir des opérations et des manipulations d’affichage. Exemple de méthode :

void CElementManager::ZoomIn(CModeler1View * pView)
{
	m_fZoomFactor += 0.10f;
	Invalidate(pView);
}

Conclusion

Pour un code orienté objet, il ne faut pas hésiter à faire des constructions, créer des classes et leur donner un minimum de responsabilités. De toute façon, si vous créé une classe et que vous n’y trouvez aucune méthode à mettre dedans, vous devrez faire machine arrière… La construction orientée objet est importante dans le développement moderne et on retrouvera les mêmes principes dans les autres langages comme Java ou C#.

Le code de l’application graphique est disponible ici : https://github.com/ChristophePichaud/UltraFluidModeler

Christophe PICHAUD
Consultant sur les technologies Microsoft

Un serveur REST Web API en C++

Introduction

Imaginez que vous puissiez développer le back-end de votre solution logicielle sur un serveur avec le roi des langages qu’est le C++ ? Sur Linux : oui. Sur Windows : oui. Un seul code portable ? Oui. Le tout sans complexité délirante juste avec des classes et des méthodes de traitement… Et on retourne du JSON pour un front-end en JS ? Chiche ! Suivez-moi, je vous explique…

Casablanca alias C++ REST SDK

Pour développer une Web API, beaucoup d’entre nous ne connaissent que ASP.NET et C#… Depuis MFC ISAPI et ATL Server, Visual C++ ne fournit plus de templates de projet pour des développements serveur. Un SDK reprend le flambeau : le REST SDK, nom de code « Casablanca ». Ce SDK écrit en C++ moderne (C++ 11) propose de réaliser du code serveur et client en utilisant les derniers patterns à savoir support du http et du JSON.

Disponible sur Github à l’adresse suivante : https://github.com/Microsoft/cpprestsdk . Ce SDK propose aussi des exemples de code pour commencer en douceur.

Un serveur http REST simple

Examinons le code minimum pour créer un serveur Web API qui retourne du JSON. Premièrement, il faut définir une classe qui contient un http_listener.

code1

Ensuite, il faut configurer ce listener en lui fournissant un URI sur laquelle on se met en écoute ainsi que les handlers pour les opérations GET, PUT, POST et DELETE. Mais avant tout, il faut déclarer le serveur comme une variable globale comme suit :

std::unique_ptr g_http;

Ensuite, il faut préparer les éléments indispensables au serveur à savoir son URI d’écoute :

utility::string_t port = U("34568");
    utility::string_t address = U("http://localhost:");
    address.append(port);

    uri_builder uri(port);
    uri.append_path(U("MyServer/Action/"));

    auto addr = uri.to_uri().to_string();
    g_http = std::unique_ptr(new MyServer(addr));
    g_http->open().wait();
    
    ucout << utility::string_t(U("Listening for requests at: ")) << addr << std::endl;

L’appel à open() active l’écoute. Reste à découvrir le code du constructeur de la classe MyServer car il faut enregistrer les handlers sur les méthodes HTTP :

code2

OK le corps du serveur http est là… Regardons le corps des méthodes HTTP. Nous allons en coder une seule mais le système est simple.

code3

Ces méthodes ne sont pas implémentées mais patience, voici ce que nous allons coder. Sur la méthode GET si on nous envoie « get_developers » alors on dump une structure de données en JSON.

Le corps du handler GET

La structure de données JSON retournée est définie comme suit :

{« job »: »Developers », »people »:[{« age »:25, »name »: »Franck »},{« age »:20, »name »: »Joe »}]}

Voyons le corps de cette méthode GET:

void MyServer::handle_get(http_request message)
{
	ucout << U("Message") << U(" ")
		<< message.to_string() << endl;

	ucout << U("Relative URI") << U(" ")
		<< message.relative_uri().to_string() << endl;

	auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
	for (auto it1 = paths.begin(); it1 != paths.end(); it1++)
	{
		ucout << U("Path") << U(" ")
			<< *it1 << endl;
	}

	auto query = uri::split_query(uri::decode(message.relative_uri().query()));
	for (auto it2 = query.begin(); it2 != query.end(); it2++)
	{
		ucout << U("Query") << U(" ")
			<< it2->first << U(" ") << it2->second << endl;
	}

	auto queryItr = query.find(U("request"));
	utility::string_t request = queryItr->second;
	ucout << U("Request") << U(" ") << request << endl;

	if (request == U("get_developers"))
	{
		Data data;
		data.job = U("Developers");
		People p1;
		p1.age = 25;
		p1.name = U("Franck");
		data.peoples.push_back(p1);
		People p2;
		p2.age = 20;
		p2.name = U("Joe");
		data.peoples.push_back(p2);

		utility::string_t response = data.AsJSON().serialize();
		ucout << response << endl;

		message.reply(status_codes::OK, data.AsJSON());
		return;
	}

	message.reply(status_codes::OK);
};

On commence par dumper les éléments reçus en entrée ? On affiche la requête, l’url demandée et les paramètres. Vous remarquerez que uri::split_query retourne une map de string string pour récupérer les paramètres de la requête. Si la requête est « get_developers », alors on déclare une structure de données et on génère du JSON.

code4

La génération du JSON

Voyons le code qui permet de générer du JSON à partir d’une structure de données. Il n’y a rien de magique, regardez :

struct People
{
	utility::string_t name;
	double age;

	static People FromJSON(const web::json::object & object)
	{
		People result;
		result.name = object.at(U("name")).as_string();
		result.age = object.at(U("age")).as_integer();
		return result;
	}

	web::json::value AsJSON() const
	{
		web::json::value result = web::json::value::object();
		result[U("name")] = web::json::value::string(name);
		result[U("age")] = web::json::value::number(age);
		return result;
	}
};

Cette structure représente un développeur. Il y a 2 propriétés : name et age. Le mécanisme de transformation d’une chaîne se fait avec les méthodes object.at().as_string() et web::json::value::string(). Pour un entier, c’est avec object.at().as_integer() et  web::json::value::number(). C’est assez simple. Pour la structure suivante, qui met en œuvre un tableau (vector), il faut utiliser d’autres méthodes :

struct Data
{
	std::vector peoples;
	utility::string_t job;

	Data() {}

	void Clear() { peoples.clear(); }

	static Data FromJSON(const web::json::object &object)
	{
		Data res;

		web::json::value job = object.at(U("job"));
		res.job = job.as_string();

		web::json::value p = object.at(U("people"));
		for (auto iter = p.as_array().begin(); iter != p.as_array().end(); ++iter)
		{
			if (!iter->is_null())
			{
				People people;
				people = People::FromJSON(iter->as_object());
				res.peoples.push_back(people);
			}
		}

		return res;
	}

	web::json::value AsJSON() const
	{
		web::json::value res = web::json::value::object();
		res[U("job")] = web::json::value::string(job);

		web::json::value jPeoples = web::json::value::array(peoples.size());

		int idx = 0;
		for (auto iter = peoples.begin(); iter != peoples.end(); iter++)
		{
			jPeoples[idx++] = iter->AsJSON();
		}

		res[U("people")] = jPeoples;
		return res;
	}
};

Pour générer un tableau en JSON, il faut utiliser la fonction  web::json::value::array. Ensuite on itère sur le vector pour associer les éléments du tableau un par un.

La partie client

Le côté client peut être développé avec n’importe quelle technologie à partir du moment où il est permis de déclencher des requêtes http et de lire du JSON. Ici, le client est écrit aussi en C++ pour vous montrer comme c’est facile d’utiliser le REST SDK :

#ifdef _WIN32
# define iequals(x, y) (_stricmp((x), (y))==0)
#else
# define iequals(x, y) boost::iequals((x), (y))
#endif

int wmain(int argc, wchar_t *argv[])
{
    utility::string_t port = U("34568");
    if(argc == 2)
    {
        port = argv[1];
    }

    utility::string_t address = U("http://localhost:");
    address.append(port);
    http::uri uri = http::uri(address);

	http_client client(http::uri_builder(uri)
		.append_path(U("/MyServer/Action/")).to_uri());

    while (true)
    {
        std::string method;
        ucout << "Enter method:";
        cin >> method;

		http_response response;

		if (iequals(method.c_str(), "get_developers"))
		{
			utility::ostringstream_t buf;
			buf << U("?request=") 
				<< utility::conversions::to_string_t(method) 
				<< U("&city=Paris");
			
			response = client.request(methods::GET, buf.str()).get();

			ucout << U("Response ") << response.to_string() << endl;

			json::value jdata = json::value::array();
			jdata = response.extract_json().get();
			Data data = Data::FromJSON(jdata.as_object());
		}
        else
        {
            ucout << utility::conversions::to_string_t(method) 
				<< ": not understood\n";
        }
    }

    return 0;
}

Conclusion

Le C++ REST SDK est facile à mettre en œuvre et permet d’avoir des fonctionnalités serveur sans difficultés particulières. Le support du JSON n’est pas trop pénible. Les opérations sont simples et la performance est au rendez-vous et ceci en standard. L’avantage de la solution proposée c’est qu’elle ne dépend pas de IIS. Le module serveur est stand-alone. Pour finaliser l’application, il faut lui ajouter une couche de service Windows et là, c’est la fête !

Christophe PICHAUD
Consultant sur les technologies Microsoft

L’univers des pure-players Microsoft

Introduction

Le contexte est le suivant: vous êtes passionnés sur les technologies d’un éditeur (IBM, Microsoft, Oracle, SAP, ou autre) ou d’une communauté dédiée (Java, C#, PHP, Linux, etc) et vous êtes arrivé au constat suivant : le monde des sociétés de services (SSII) ne répond pas à vos attentes. Ils existent plusieurs facteurs et plusieurs mythes et le décor ressemble à une pièce de théâtre : le PDG, le Directeur des Opérations, la DRH, le Directeur Technique (CTO), les commerciaux et les collaborateurs, appelés « consultant » dans de nombreuses sociétés mais rien que le terme nécessiterait un article de 12 pages…. Donc voilà le décor et le constat est souvent le même : un commercial vous a placé chez un client ; la mission n’est pas forcément plus intéressante que cela ; le code est ancien ; les outils sont obsolètes ; le métier n’a rien de fun… A la pause déjeuner, vous mettez à jour votre profil Viadeo/LinkedIn (chacun sa chapelle) et vous tombez sur un chasseur de tête qui vous envoie un message qui ne vous laisse pas insensible : « et si vous exprimiez totalement votre talent dans une société spécialisée dans les technologies X, un pure-player ! ». Avouez que vous avez déjà eu l’occasion de tomber sur genre de messages ; éventuellement d’un cabinet Anglo-saxons (ou proche de Neuilly sur Seine) via lequel vous allez faire une expérience particulière : il existe un monde autre que celui des sociétés SSII traditionnelles. Ce ne sont pas des grands cabinets de conseils où il y a des profils divers issues non pas d’école d’Informatique mais de HEC ou autres mais ce sont des pure-players. Côté immatriculation, ces sociétés sont des entreprises de type PME avec un truc qui les différencient des autres : elles choisissent leur client et vous ne ferez que des missions sur les technologies ou produits que vous adorez !

Cela ne peut pas être pire que la SSII !

Le monde des SSII n’est pas forcément très agréable ; les projets de TMA ou les centres de services dédiés chez les clients ne permettent pas l’épanouissement de tous les collaborateurs. Donc, quel est le risque de rejoindre un pure-player si ce n’est baigner dans un monde consanguin dans lequel on va accompagner le client sur un domaine que l’on connaît (ou sur lequel on va être très fort) et sur lequel on aura toute la latitude pour répondre aux besoins du client. Et puis franchement, si cela ne colle pas, le monde des SSII est toujours prêt à accueillir des têtes bien faites donc l’aventure vaut le coup d’être tenté.

Le domaine des RH du monde des PME

Un grand groupe possède un département RH souvent froid. Les entretiens sont parfois surréalistes et on a l’impression d’avoir affaire à des assistance RH de type étudiantes en psychologies certaines fois. C’est vraiment le point faible des SSII… Dans un pure-player, l’humain est mis au centre et on accueille le candidat comme il est. Comme au Mc Do? Oui presque… En fait, il faut se plier à un code de valeurs…

Des PME aux méthodes de multinationales

Dans ces sociétés, le mot « valeur » est omniprésent. On parle aussi de « policy ». Il y a des règles pour tout : l’éthique, le commerce, le consulting, le management, etc. Au premier abord, ce n’est pas un problème. Le pure-player est moderne : il favorise la mobilité et fournit les accessoires nécessaires : ordinateur portable et smarphone. Souvent, il est proposé aux collaborateurs un plan de formation (« training ») qui sort de l’ordinaire : passage de certifications, animation de conférences, écriture de blogs, animations marketing. L’autre approche attractive, ce sont les évènements internes pour rendre les collaborateurs dynamiques et pour qu’ils puissent échanger. Ces méthodes existent depuis les années 50/60 et sont issues des cabinets privés et des confréries étudiantes anglo-saxonnes. La règle c’est la communauté et chaque nouveau collaborateur se voit attribuer un  parrain (budy) qui sert à nous faire évoluer au sein de l’entreprise. C’est le grand frère, le confident, le partenaire.

Les premières missions

L’avantage du pure-player, c’est que le monde client est strictement balisé : CLIENT + TECHNOLOGIES. Les collaborateurs ne s’occupent que de technologies et ne veulent que cela et le domaine contractuelle et commerciale est entièrement à la charge d’un département et dont le contenu est tenu secret ; chacun son métier. C’est une différence avec le monde du service où le chef de projet peut passer son temps à mixer techniques et contraintes budgétaires et… c’est moins drôle pour celui qui veut la meilleure solution pour répondre aux besoins du client. Les premières missions sont un moyen pour le collaborateur de monter très vite en compétence sur son domaine de prédilection. Après, la réalité (et le contexte) client peut amener le quotidien à ressembler au monde de la SSII mais l’avantage du pure-player c’est que l’on travaille sur un domaine qui nous plait donc ça change tout. Après quelques missions ; les missions d’un pure-player ne durent jamais très longtemps ; le consultant/l’expert peut se sentir à l’étroit dans son rôle d’Expert Technique. C’est là que certains se posent des questions…

Passer un cap

En SSII, la DRH va vous demander si vous voulez faire du management ou rester dans la technique… Chez un pure-player, ce n’est pas ça du tout ! On va vous amener à vous exprimer sur le domaine qui vous intéresse : le commerce, l’audit, la formation, les migrations, l’organisation ou le conseil. Et là, c’est un domaine dans lequel le métier est très très très plaisant. Dans le monde de la SSII, un chef de projet va être confronté à des grilles tarifaires, du référencement, des contraintes. Un pure-player, c’est un couteau suisse ; c’est le plombier qui va colmater la fuite. Donc, il fait vite et bien. C’est pour cela que l’on fait appel à lui.

Choisir son arme

En SSII, la DRH va vous demander si vous voulez faire du management ou rester dans la technique… Chez un éditeur de taille mondiale, le consulting est balisé et chaque détail a son importance. Les documents sont truffés de « No Warranty » et « copyrights ». De plus, les consultants créatifs ne sont pas les bienvenus car tout doit être identique et uniforme. Dans une PME, il est possible d’avoir sa personnalité (voir même un fort caractère). Après, la tenue vestimentaire peut être une barrière. Suivant votre style vous êtes un DBA tee-shirt jean ou un consultant BI costard cravate ou bien si comme moi vous faites le yo-yo avec la balance, le pantalon bège Chino ou en toile passe partout. Bon j’avoue, la veste au-dessus est parfois nécessaire ! Passons sur le « dress code » car seuls les commerciaux sont réellement assujettis à cela avec les Managers. Au bout de 2 ou 3 années chez un pure-player, même un collaborateur de moins de 30 ans peut être un « Consultant Sénior ». Mais attention, c’est ici que cela peut poser problème car il faut choisir son chemin… La progression chez le pure- player va se traduire sur la capacité du collaborateur à amener de la valeur ajoutée. Il y a plusieurs moyens de faire jouer la montre en restant sur un produit unique et être dédié à celui-ci dans le support, les mises à jour, les migrations ou bien partir dans le domaine du coaching/manager et là on va tranquillement se rapprocher du monde des SSII. Car si certains éditeurs sont de tailles importantes, des sociétés sont de petites structures et les dépendances vis-à-vis des clients amènent à faire des choix qui vont au-delà de la technologie….

L’envers du décor

Lorsque que le client participe à une part importante du chiffres d’affaires de la société, le monde devient plus complexe ; plus réel. Les choix deviennent des enjeux et là, la technologie est un atout et aussi une arme. Dois-je dire à mon client de passer à la dernière version où dois-je encourager mon client à développer sur la librairie X prônée par mon éditeur favori…? Et là, on rentre dans une dimension autre que le conseil en technologie, cela devient de l’Architecture avec un grand A et là, l’arène (comme à Rome) est le lieu de tous les dangers. Il y a  plusieurs écoles suivant que vous appartenez à un éditeur ou un intégrateur.

Editeurs et Intégrateurs

Si vous optez pour un éditeur logiciel, vous êtes corps et âme pour le « bien-être » du produit donc vous ne prenez pas de risques car vous savez ce que peux et ce que ne peux pas le dit produit. Si vous êtes chez un intégrateur, là c’est parfois compliqué car vous faites le lien et devez faire des choix. Le service ou celui qui a les moyens de tout changer c’est l’Architecte…

L’Architecte

Si vous êtes membre d’un pure-player, vous êtes souvent l’architecte et vous avez des marges de manœuvres. Si vous êtes intégrateur, on peut vous forcer la main. Chez un éditeur, vous êtes à l’abri car vous pouvez vous retrancher derrière de nombreuses raisons (fumeuses) pour contourner les obstacles comme la sécurité, le support, le by-design, le mode de license, etc.

Le Conseil

Lorsqu’un client fait appel à un pure-player, il s’adresse à un connaisseur, un expert. Il attend donc des répondes à des besoins qui dépassent le stade du prototype « hello world ». Donc souvent, le client demande au pure-player de le mettre dans les meilleurs conditions pour que la solution / le projet respecte les bonnes pratiques et que les coûts, les performances et la montée en charge soit au niveau affiché sur les plaquettes commerciale de ladite société ! Et là, il n’est pas rare de rencontrer des Architectes inexpérimentés qui vont vendre de la technologie au client dans le seul but d’utiliser le dernier produit / la dernière librairie en vue / à la mode… Et là, le risque de chuter n’est pas perceptible au premier abord mais est inévitable car le pure-player est obligé de rédiger des documents : les fameux livrables. Ces documents font foi et valent paiement de facture /prestation en retour. Ces documents sont soumis souvent à un accès restreint mais à disposition des Directeur Technique ou du DSI ou des chefs de services et peuvent être utilisé pour un audit ou (re)prendre le contrôle d’un système d’information dans sa globalité. Prenons un exemple : vous êtes le client et votre pure-player favori vous préconise de positionner son EAI favori au centre de votre SI en 2010 ; vous allez greffer de nombreux modules autour et ceci peut valoir une externalisation de votre SI en totalité sur un audit 4 ou 5 ans plus tard car vous avez « maqué le client » avec un produit qu’il n’avait pas besoin mais sur lequel vous avez dû avoir recours à des experts dédiés à cet EAI… Bref, une mauvaise décision d’architecture peut devenir un enjeu stratégique et pourtant cela peut être fun de mettre en place une « customisation » du moteur de workflow de votre EAI préféré… Si vous jouez trop à court terme, vous perdez et malheureusement c’est le cas de beaucoup de pure-player dans mon écosystème (MS). Je vois tellement de chefs de service qui ne veulent plus de ces sociétés d’ « experts » car les prestations les ont menés dans le mur… Après, la faute est-elle partagée ou bien juste le fruit de circonstances pour faire une bonne affaire commerciale ou un gros coup. Ne choisissez pas de structures trop petites si vous êtes jeune et mettez-vous à l’abri de ce que je nomme « les deals foireux ».

L’Architecture aura votre peau !

Dans les années 2000, il était possible de vendre tout et à n’importe quel prix. Les délires technologiques les plus fous ont été mis en œuvre.  Vous n’avez jamais entendu parler du Web service qui traverse un autre web service et puis finalement on repart vers un autre… Passons.

Maintenant, les services achats ont pris le contrôle et les centres de services sont légions. Les grandes sociétés Informatique récupèrent la totalité des développements des grands comptes et il est de plus en plus difficile de faire travailler les petites sociétés et les pures-player et les grands éditeurs sont rois. Par contre, les grandes SSII font un retour sur le devant de la scène car elles empochent les gros contrats. Tout est de plus en plus concentré. Donc finalement, faut-il retourner en SSII ??? Par forcément, mais le métier évolue et il n’est plus possible de vendre de « la technologie » comme de « la lessive ». Le client n’achète plus de la technologie. C’est fini. Le pain blanc a été mangé donc quoi ? Maintenant, faut être éthique et pro. C’est la seule condition pour durer. Et là, il faut s’adapter. C’est une composante du métier ; il faut se renouveler. Mon client a sous-traité ses développements en Inde ou au Maroc par contre, j’ai besoin de lui donner des conseils sur pleins de choses. C’est de l’Interop oui mais pas comme il y a dix ans. Il faut savoir expliquer à son client que certaines technologies ne sont pas pérennes et parfois savoir brieffer celui qui fait l’avant-vente pour lui expliquer qu’il faut qu’il arrête de vendre des tapis chez votre client sinon c’est perdu pour tout le monde au prochain audit groupe.

L’exemple Microsoft Silverlight – le choc

De nombreuses entreprises ont accueillies avec stupeur le fait que la technologie Silverlight de Microsoft était arrêtée. Le message de Microsoft pour les développeurs n’est pas limpide depuis quelques années et les clients qui ont investis sur cette technologie connaissent de grands moments de solitude! Donc quel est le retour d’un pure-player qui a vendu du SilverLight les 5 dernières années à ses clients… Le résultat est désastreux et c’est un pan entier de la profession qui « trinque ». D’un autre côté, il faut appeler un chat un chat : le marketing porte une énorme responsabilité dans cette affaire et la réalité des clients et leurs calendriers ne collent pas à celui des éditeurs qui cherchent à vendre des outils de développement et des technologies… OK en 2011 Microsoft passe aux applications Windows Store mais mes clients (CAC40) viennent de passer à VS2010 et Windows 7 donc… Y une c.. dans le potage !

L’exemple Azure

Dans le monde Microsoft, vous avez certainement entendu le slogan « Mobile first, Cloud first » du CEO de Microsoft. Oui mais bof bof bof… Vous avez beaucoup de clients sur Azure, le cloud de Microsoft ? Et des clients sur Windows Phone, ou sur tablette Surface ou pour des applications Windows Store ? Et bien non… Les clients sont en Windows 10 à 90% et les développements Windows Phone sont une niche (très petite). Dans ce genre de technologies, les pure-players ne sont pas à la fête… Donc soyez vigileant. Pour faire l’Azure, optez pour travailler directement chez l’éditeur sinon ça va être compliqué. Y a les sociétés de type hébergeur classique comme Orange mais bon… le monde du cloud vaudrait un article tellement c’est cocasse. Passons.

Les valeurs sures

Le monde de la base de données reste le domaine le plus confortable. Il y a des bases partout, soit pour de l’accès standard soit pour du BI ou du reporting… Le monde du système est aussi un domaine très stable car configurer un Active Directory, un cluster Windows, une ferme Web IIS sont des scénarios présents chez de nombreux clients. Malheureusement dans notre monde du développement logiciel, il n’y a que peu de domaine stable. Le monde SharePoint possède bien sa niche, le monde CRM aussi mais ce n’est pas destiné à fournir une carrière bien remplie à mon avis. Quid du développement classique ? L’avenir n’est pas au développement mais à l’Architecture.

 

L’Architecture

L’avenir est à l’Architecture et aux fondamentaux. Les clients ont recours à des développeurs à bas prix et ceux-ci ne sont pas amener à prendre des décisions ou faire des choix technologique mais juste suivre (bêtement ?) un modèle. Donc voici mon conseil : suivez les divers voies du monde de l’Architecture avec un grand A car cela peut vous emmener très loin. Exemple : si votre client possède une base SQL Server de 10 MB, ok ; on part tête baissée sur une stratégie d’accès de type Entity Framework mais si elle fait 1 ou 2 TB, on fait quoi ? Ah là, je sens certains pure-player se retirer du jeu… A un moment donné, la technologie nous amène à choisir un camp (ou une stratégie) : vendre un service onéreux avec une logique court-terme ou bien orienter le client vers une solution pérenne qui est la moins mauvaise de toutes les bonnes solutions. Et là, il faut chuter plusieurs fois pour apprendre. Cela devient un deal. Et à ce moment-là, l’architecture devient du conseil sur les technologies et la démarche n’est plus de vendre la dernière technologie du moment. Ne méprisez pas les Architectes qui ne codent plus mais si vous travaillez avec un Architecte qui code, essayer d’en faire un allier – c’est qu’il ne peut pas être foncièrement un mauvais bougre ! Si votre but est de durer dans le métier, faite attention à ne pas tomber dans le piège de certaines grosses compagnies qui via l’avant-vente pratiquent « la politique de la terre brulée » c’est-à-dire : ok on bosse ensemble mais on réécrit tout à la dernière mode. Ce temps-là est révolu.

Conclusion

Dans le monde du service, certains pure-players sont très attractifs. Dans l’écosystème Microsoft par exemple, il y a des spécialistes SharePoint ou CRM, SQL Server et SSIS, Biztalk, ou les rois du WPF, PRISM, EntLib, EF. Certains font de l’ASP.NET MVC ou du Windows Phone / Windows Store Apps et même du multiplateforme avec Xamarin. Il y a de quoi bien s’éclater et cela n’as pas de prix. Après, c’est à vous de trouver votre maison. Ne négligez pas les SSII traditionnelles qui ont parfois des pôles dédiées à ces technologies et qui sont « à part » dans la SSII ; c’est souvent un bon compromis. Bref, je suis pour les pure-player !  Si vous êtes pressé et que vous voulez en CV « à l’arrache » voici mon conseil (de mémoire): Nélite, Orange Business Services, AI3, Avanade, Microsoft Consulting Services, AvePoint, Sopra, MCNEXT, Soat, Infinite Square, Wygwam, NEOS-SDI, Exakis, Azeo, CAST, Cellenza, Wiwnise, et Sogeti.  N’hésitez pas à passer « à l’ennemi » ; j’ai rencontré des gens très bien chez IBM ou Bull.

 

Pourquoi C++ en 2017 ?

Pourquoi C++ en 2017 ?

Introduction

Pourquoi utiliser C++ en 2017 ? La question est posée… C++ est un descendant de C, regardez :

Pour rappel, tous les logiciels que j’utilise sur mon PC portable sont écrits en C/C++ :

  • Windows et ses 3400 dlls
  • Explorer l’explorateur de fichiers
  • Le gestionnaire de tâches
  • MS Paint
  • Mon lecteur multimédia
  • Les navigateurs Web (Chrome, Firefox, Internet Explorer, Edge)
  • La suite Office (Word, Excel, PowerPoint, Outlook, etc)
  • Notepad++
  • Acrobat Reader
  • Mon vieux MSDN Library 2008 SP1

La seule exception que j’ai sur mon PC est Visual Studio qui est une application hybride C/C++ .NET COM.

Le PC portable que j’utilise pour écrire cet article est un portable low-cost acheté en 2015 :

  • Processeur Intel Celeron Quad Core
  • Intel HD Graphics
  • 4 GB RAM
  • 500 GB Disk

Et pourtant avec un PC low-cost peu puissant, je peux toujours utiliser en même temps Windows, un browser, Word, MS Paint, Visual Studio et écouter de la musique en arrière-plan. Et je n’ai pas de soucis de performance ; la preuve voici le gestionnaire de tâche :

Bien sûr, ce PC ne fera pas de montage vidéo ou de la compression diverse et variée… Mais par contre, pour une utilisation standard, il me permet de travailler.

Anatomie des applications

Les applications que je lance consomment peu de mémoire et sont réactives. Pourquoi ? Parce qu’elles utilisent directement les APIs du système d’exploitation qu’est Windows. Les APIs Windows sont utilisables depuis un simple compilateur C/C++. Il suffit de faire #include <windows.h> et vous avez accès à des milliers de fonctions. Chaque édition de Visual Studio distribue le SDK Windows. Les applications consomment peu de mémoire et ça c’est la clé de la réussite.

Mythes et réalités

Vous allez me dire, ouaip mais faire une application en C++ from scratch, c’est difficile et ce n’est pas productif comme langage le C/C++ hein ? Je réponds de faire attention au discours du marketing et aux préjugés…

En effet, pour développer une application, Microsoft préconise de faire du C#, VB.NET ou JavaScript. Oui mais bon… la question est la suivante : pourquoi Microsoft ne fait pas ses applications en .NET ? On nous avait dit que Microsoft pratiquait le dog-fooding : c’est-à-dire que lorsque MS sort une technologie, MS la réutilise dans ses produits. Ah oui, mais c’est un peu plus complexe que ça. Pour Microsoft, le C++ c’est l’électricité, c’est naturel. Pour le marketing, .NET c’est limpide. Donc, qui croire ? Si vous voulez faire une application qui ne fonctionne que sous Windows, oui .NET peut-être une alternative mais attention, il faut redistribuer le framework .NET. Vous allez me dire oui mais avec .NET Standard 2.0, on peut compiler sous Windows, Mac et Linux. Je vais vous la faire courte… Il y a 10 ans, il existait un package nommé Mono qui permettait de faire du C# sous Linux. Ça a été un flop. Maintenant que Microsoft a racheté la société Xamarin et que .NET Standard 2.0 est sorti, le marketing nous (r)envoie ses sirènes pour faire des applications mobiles et desktop via .NET et C# sur plusieurs plateformes… Méfiez-vous des effets d’annonces car ce n’est pas aussi simple que cela est énoncé. Faites-vous votre propre expérience. En C/C++ sous Windows, il suffit de redistribuer MSVCRedist.exe qui fait 10 MB. Ce package contient la runtime du C, la STL, les DLLs des MFC. De plus, Windows contient déjà plusieurs versions de ces modules.

Pour faire une application de bon standing, il faut être capable de fournir une interface graphique élégante, des librairies qui consomment peu de mémoire et des opérations qui ne poussent pas le processeur dans ses derniers retranchements. Et là, c’est le principe des couches. Si vous développez avec un runtime et un framework, il y aura plusieurs couches à traverser avant que votre code ne rejoigne les APIs du système d’exploitation ou les instructions de votre processeur. La mode est de préserver la batterie, de soulager les disques, et de consommer peu de mémoire. Seul le C/C++ vous permet cela. Au niveau du langage C, on trouve les fondamentaux suivants :

  • Gestion de la mémoire avec les fonctions malloc/free
  • Gestion des I/O : stdio.h et les fonctions open, close, read, write, fopen, fprintf, fread, fwrite
  • Gestion des pointeurs int * ptr;
  • Etc

Le C++ fournit un niveau plus abstrait via la STL (Standard Template Library) qui gère :

  • La gestion automatique de la mémoire : on évite new/delete via unique_ptr<T> et shared_ptr<T>
  • Gestion des strings et des buffers avec string, wstring et les streams
  • Gestion des containers (array, vector, list, map, set, etc)
  • Des d’algorithmes
  • Etc

Au lieu de présenter mon PC portable low-cost, je pourrais vous parler des distributions Linux qui tournent sur de vieilles machines. Pourquoi ? Tout est fait en C/C++ à 99%. Bref, on positionne le curseur sur les éléments suivants :

  • Gérer la batterie
  • Gérer la mémoire
  • Gérer le processeur

Avec les langages dit productifs, vous n’avez aucun moyen d’optimiser ces 3 facteurs car vous êtes dans les hautes sphères d’un runtime (CLR ou JRE) qui fait pleins de choses tout seul… Sur un poste de bureau, on peut cacher cela, mais sur un mobile, la sanction est immédiate. Si l’application suçote la batterie ou si une application passe son temps à chauffer la batterie, ce n’est pas bon et l’application n’aura pas un grand succès.

Le C/C++ est portable et natif

Sur chaque système (Windows, Linux, Mac), il existe un compilateur C/C++. Il est ainsi possible de faire du code portable. Bien sûr, le code qui utilise les API Windows ne compilera pas sur Linux mais le code métier, les classes Poco et les libs third-party compilent. L’avantage du C/C++ est que le système d’exploitation est fait avec du C. Les autres APIs des librairies third-party sont aussi faites en C/C++. Donc quel est cet avantage ? Sous Windows, vous voulez envoyer par exemple des messages cryptés ; rien ne vous interdit de récupérer une librairie de chiffrement sur Linux, de la recompiler, et de l’utiliser sous Windows. Les combinaisons sont infinies.

De plus, les compilateurs C/C++ sont gratuits.

La documentation

Si vous voulez débuter, il existe une ressource terrible : MSDN Library 2008 SP1. Cet ensemble d’articles contient toutes les ressources pour développer sous Windows. On y trouve aussi des articles sur le C/C++ et les MFC.

Téléchargez ce fichier ISO de 2.8 GB depuis le Microsoft Download Center et installez-le.

Il existe aussi une version plus récente de la documentation Microsoft. Avec Visual Studio, il existe une application nommée Help Viewer et dans laquelle il est possible de télécharger différentes sections de l’aide (.NET, Visual C++, SQL Server, etc).

L’IDE

Là, le choix se porte sur Visual Studio. VS existe en version gratuite dite Community Edition. Cette version est le produit d’appel vers la version payante de VS comme VS Pro. Le coût du produit est environ 500€.

L’IDE Visual Studio est très confortable mais il faut bien comprendre que ce produit contient moult fonctionnalités. Le problème de Visual Studio est qu’il est de plus en plus lourd et donc il faut savoir patienter si votre machine de développement n’est pas très rapide… Bref, vous êtes prévenu !

Il est possible aussi d’utiliser VS Code mais ce n’est pas pareil…

Par où commencer ?

Le premier pas est d’apprendre le langage dans sa mouture C++ 11 ou C++ Moderne. Un bon ouvrage, écrit par le créateur du langage, est disponible depuis 2014 : A Tour of C++ par Bjarne Stroustrup.

La STL (Standard Template Library)

La STL est la bibliothèque du C++. Elle contient de nombreux fichiers d’entêtes. L’évolution du compilateur est indissociable de l’évolution de la STL. La STL fournit des classes templates pour satisfaire tous les besoins principaux : cela va des algorithmes aux containers, des strings au threads, etc. Voici les fichiers d’en-têtes :

Les classes

Il est possible de créer des types concrets ou abstraits. Pour créer une classe abstraite, il suffit de déclarer une méthode virtuelle pure. L’héritage peut être simple ou multiple.

Les templates

Un template est une classe qui prend en paramètre un ou plusieurs types. Le template est évalué à la compilation. Il existe aussi des fonctions template.

Le C++ Moderne

Voici les principaux éléments du C++ moderne :

  • Utilisation de la stack plutôt que le heap
  • Utilisation de auto pour masquer le type réel
  • Utilisation des smart pointers à la place des pointeurs normaux
  • Utilisation des types std::string ou std::wstring à la place de char[]
  • Utilisation des containers de la STL vector, list et map à la place des structures customs
  • Utilisation des algorithmes de la STL à la place des routines customs
  • Utilisation des exceptions pour remonter des erreurs
  • Utilisation du type STL std::atomic<> à la place des mécanismes IPC custom
  • Utilisation des lambdas inline au lieu des petites fonctions déclarées séparément
  • Utilisation du range for pour le parcours des tableaux et autres containers

L’utilisation du mot-clé auto permet de masquer des types complexes à utiliser. Ainsi pour déclarer un itérateur sur un vector de shared_ptr<T>, il ne faut plus écrire :

vector<shared_ptr<MyClass>>::const_iterator it = v.begin();

A la place on écrit ça: auto it = v.begin();

Avec le C++ moderne, il n’y pas besoin d’utiliser les mécanismes de new/delete pour allouer ou libérer de la mémoire. On utilise, à la place, des smart pointers comme unique_ptr<T> ou shared_ptr<T> qui savent libérer la mémoire automatiquement.

Le développement multi-plateforme

Revenons sur deux définitions qui sont galvaudées : le natif et le multi-plateforme. Quand on parle de développement natif, on parle de développement qui utilise le même langage que celui qui a permis de faire le système d’exploitation. Concrètement c’est du C/C++. Sous Windows, Linux et Mac, c’est comme ça.

Ça veut dire que l’on utilise directement les APIs du système d’exploitation qui sont exploitées dans les entêtes .h fournit par le SDK de l’OS. Pour un programme Windows, cela veut dire qu’il charge kernel32.dll, user32.dll et gdi32.dll. A partir du moment où vous utilisez un langage comme C# qui contient un garbage collector et utilise un runtime, vous n’êtes pas dans le natif. Malheureusement, le marketing raconte des bobards…

Le développement hybride C++ /CLR

Sous Windows, il est possible de faire du code C++ et du code qui appelle des classes .NET via le C++/CLI. Pour faire cela, il faut que le projet supporte l’option Common Language Runtime Support /clr. Il est ainsi possible de faire des ajouts de référence à des assemblies .NET dans un projet C++. Si vous avez du code .NET, vous pouvez le réutiliser et cela se fait très facilement. L’avantage c’est que si vous avez des besoins XML ou ADO.NET, vous aurez de suite le support du .NET Framework à portée de main.

Framework graphique

Pour développer une application graphique, il faut avoir des APIs pour afficher des contrôles, des fenêtres, des menus, des boites de dialogue, etc. Chaque système (Windows, Linux, Mac) fournit des APIs pour cela. Il existe aussi différents Frameworks pour arriver à faire des applications fenêtrées :

  • GTK
  • Qt
  • MFC
  • WTL
  • AppKit, Cocoa
  • WxWidgets

Sous Windows, il y a 3 options :

  • GDI32 : API C historique de gestion des fenêtres
  • MFC : Microsoft Foundation Classes
  • WTL : Windows Template Library

Personnellement, j’ai commencé avec GDI32 pour apprendre les bases. De nos jours, je suis un adepte des MFC car je suis très productif avec. Le framework fournit les bases des applications modernes à la sortie du Wizard. Avec GDI32, il faut tout coder et cela prend du temps. De plus, MFC fournit des contrôles sophistiqués comme les docking pane, les properties grid, le Ribbon, les MDI avec un modèle document-vue ; c’est un peu différent du MVC mais les principes sont les mêmes.

Voici le type d’applications que l’on peut faire facilement avec les MFC :

Cette application possède tous les contrôles graphiques qui sont sexy et fonctionnels.

Le Back-end en C++

Il est possible de construire la partie serveur d’une application via plusieurs technologies. Il est possible de construire :

  • Un service Windows avec des IPC
  • Un serveur XML-RPC custom
  • Un serveur Web API qui retourne du JSON via le REST SDK

L’avantage du REST SDK, c’est qu’il peut retourner des data au format JSON pour un front qui n’est pas forcement en C++. Le serveur C++ fait avec le REST SDK fera l’objet d’un article séparé.

Conclusion

Sous Windows, le compilateur Visual C++ fête ses 25 ans ! Il n’a jamais été autant surpuissant. Le C++ Moderne ouvre de grandes possibilités car le code est plus simple à écrire, plus lisible et plus performant. La STL a de nombreuses fonctionnalités à utiliser comme les containers, les smart pointeurs. Bref, le langage est très abordable même si vous avez des bases de Java ou C# car ceux-ci ne sont que des descendants du C++…