# Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez créer et manipuler des fonctions d'extension de la machine virtuelle.

Le temps de lecture de ce didacticiel est estimé à 35 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier fonctions.svm_plugin en utilisant ce code :

    PLUGIN func

    DEFINE

# Définition

Tout comme les structures sont des sortes de types réservés aux extensions, les fonctions sont des sortes d'instructions réservées aux extensions. Il est donc possible d'appeler ces nouveaux objets uniquement depuis les extensions, mais pas depuis le code de la machine virtuelle.

## Paramètres

Une fonction se définit un peu de la même manière que les instructions, avec quelques différences :

- Les fonctions n'ont pas de drapeaux `WAITING` et `SYSTEM`.
- Les paramètres suivent toujours une expression rationnelle, mais avec des éléments en plus :
  - des structures peuvent être passées en précisant leur type avec `$plugin.entry` (le signe $ signifiant ici "structure"),
  - le mot-clef `STRUCT` indique une structure de n'importe quel type,
  - le mot-clef `VARIABLE` indique une variable Simple Virtual Machine (qui deviendra une variable locale de la fonction appelée),
  - le mot-clef `.` fonctionne aussi avec les structures et les variables Simple Virtual Machine.

Modifiez le code de l'extension :

    PLUGIN func

    DEFINE

    STRUCT func.a
    %{
    	std::string _s;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.a $func.a MUTABLE STRUCT
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Structure b = ::svm_parameter_structure_get(svm,argv[1]);
    	SVM_Value_PluginEntryPoint t = ::svm_structure_get_type(svm,b);
    	if(::svm_value_is_equal(svm,t,CONST_PEP(func,a)))
    	{
    		auto rb = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,t,b));
    		rb->_s = a->_s;
    	}
    %}

    FUNCTION func.b MUTABLE $func.a VARIABLE
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	auto c = ::svm_kernel_get_coredump(svm,k);
    	a->_s = RAW_STRING(c);
    %}

## Valeur de retour

Une fonction peut avoir une valeur de retour, tout comme les instructions. Et encore une fois, les fonctions supportent en valeur de retour les structures et les variables.

Modifiez le code de l'extension :

    PLUGIN func

    DEFINE

    STRUCT func.a
    %{
    	std::string _s;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.a $func.a MUTABLE STRUCT
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Structure b = ::svm_parameter_structure_get(svm,argv[1]);
    	SVM_Value_PluginEntryPoint t = ::svm_structure_get_type(svm,b);
    	if(::svm_value_is_equal(svm,t,CONST_PEP(func,a)))
    	{
    		auto rb = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,t,b));
    		rb->_s = a->_s;
    	}
    %}

    FUNCTION func.b MUTABLE $func.a VARIABLE
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	auto c = ::svm_kernel_get_coredump(svm,k);
    	a->_s = RAW_STRING(c);
    %}

    FUNCTION func.c STR -> $func.a
    %{
    	auto s = ARGV_VALUE(0,string);
    	return NEW_STRUCT(func,a,new struct_a { RAW_STRING(s) });
    %}

    FUNCTION func.d INT -> VARIABLE
    %{
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal(svm,z,AUTOMATIC,i);
    	return z;
    %}

Vous pouvez générer et compiler l'extension. Mais comme les fonctions ne sont pas appelables depuis le code, il est encore pas possible de les utiliser.

- Les fonctions d'extension sont des sortes d'instructions privées aux extensions.
- Les fonctions acceptent en paramètre et en valeur de retour des structures et des variables, avec leurs jokers respectifs.

# Appel de fonction

Le seul objectif d'une fonction est d'être appelée. Et comme elle ne peut l'être que depuis les extensions, cela signifie que cet appel doit passer par une fonction de l'interface programmatique.

## Création des paramètres

La première étape d'un appel de fonction est la création de la liste des paramètres à passer à la fonction.

Modifiez le code de l'extension :

    PLUGIN func

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    STRUCT func.a
    %{
    	std::string _s;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.a $func.a MUTABLE STRUCT
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Structure b = ::svm_parameter_structure_get(svm,argv[1]);
    	SVM_Value_PluginEntryPoint t = ::svm_structure_get_type(svm,b);
    	if(::svm_value_is_equal(svm,t,CONST_PEP(func,a)))
    	{
    		auto rb = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,t,b));
    		rb->_s = a->_s;
    	}
    %}

    FUNCTION func.b MUTABLE $func.a VARIABLE
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	auto c = ::svm_kernel_get_coredump(svm,k);
    	a->_s = RAW_STRING(c);
    %}

    FUNCTION func.c STR -> $func.a
    %{
    	auto s = ARGV_VALUE(0,string);
    	return NEW_STRUCT(func,a,new struct_a { RAW_STRING(s) });
    %}

    FUNCTION func.d INT -> VARIABLE
    %{
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal(svm,z,AUTOMATIC,i);
    	return z;
    %}

    INSTRUCTION func.example
    %{
    	auto s = NEW_STRUCT(func,a,new struct_a { "default value" });
    	SVM_Parameter *parameters = ::svm_parameter_array_new(svm,2);
    	parameters[0] = ::svm_parameter_structure_new(svm,s);
    	parameters[1] = ::svm_parameter_variable_new(svm,CURRENT(kernel));
    %}

Les paramètres sont préparés dans un tableau créé via la fonction `svm_parameter_array_new`, et chaque valeur du tableau doit être initialisé avec le résultat d'une fonction `svm_parameter_type_new`.

## Appel

Les paramètres étant prêts, passons à l'appel proprement dit : il se réalise avec la fonction `svm_function_call`.

Modifiez le code de l'extension :

    PLUGIN func

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    STRUCT func.a
    %{
    	std::string _s;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.a $func.a MUTABLE STRUCT
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Structure b = ::svm_parameter_structure_get(svm,argv[1]);
    	SVM_Value_PluginEntryPoint t = ::svm_structure_get_type(svm,b);
    	if(::svm_value_is_equal(svm,t,CONST_PEP(func,a)))
    	{
    		auto rb = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,t,b));
    		rb->_s = a->_s;
    	}
    %}

    FUNCTION func.b MUTABLE $func.a VARIABLE
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	auto c = ::svm_kernel_get_coredump(svm,k);
    	a->_s = RAW_STRING(c);
    %}

    FUNCTION func.c STR -> $func.a
    %{
    	auto s = ARGV_VALUE(0,string);
    	return NEW_STRUCT(func,a,new struct_a { RAW_STRING(s) });
    %}

    FUNCTION func.d INT -> VARIABLE
    %{
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal(svm,z,AUTOMATIC,i);
    	return z;
    %}

    INSTRUCTION func.example
    %{
    	auto s = NEW_STRUCT(func,a,new struct_a { "default value" });
    	SVM_Parameter *parameters = ::svm_parameter_array_new(svm,2);
    	parameters[0] = ::svm_parameter_structure_new(svm,s);
    	parameters[1] = ::svm_parameter_variable_new(svm,CURRENT(kernel));
    	::svm_function_call(svm,CONST_PEP(func,b),2,parameters);
    	auto rs = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,CONST_PEP(func,a),s));
    	std::cout << "###" << std::endl << rs->_s << "###" << std::endl;
    %}

Notez que l'appel à la fonction se fait en la nommant avec son point d'entrée d'extension : il est alors possible d'appeler une fonction définie par une autre extension ! C'est la machine virtuelle qui fera alors la résolution vers la fonction C/C++ implémentant cette fonction.

Pour les paramètres, deux informations sont données : le nombre de paramètres, et le tableau de paramètres.

Vous pouvez générer et compiler l'extension. Cette fois, créez une application appelant uniquement l'instruction `:func.example` pour voir l'appel de fonction se réaliser.

## Gestion de la valeur de retour

Lorsque la fonction retourne une valeur, cette valeur est simplement retournée par la fonction `svm_function_call`.

Modifiez le code de l'extension :

    PLUGIN func

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    STRUCT func.a
    %{
    	std::string _s;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.a $func.a MUTABLE STRUCT
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Structure b = ::svm_parameter_structure_get(svm,argv[1]);
    	SVM_Value_PluginEntryPoint t = ::svm_structure_get_type(svm,b);
    	if(::svm_value_is_equal(svm,t,CONST_PEP(func,a)))
    	{
    		auto rb = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,t,b));
    		rb->_s = a->_s;
    	}
    %}

    FUNCTION func.b MUTABLE $func.a VARIABLE
    %{
    	struct_a* a = ARGV_STRUCT(0,func,a);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	auto c = ::svm_kernel_get_coredump(svm,k);
    	a->_s = RAW_STRING(c);
    %}

    FUNCTION func.c STR -> $func.a
    %{
    	auto s = ARGV_VALUE(0,string);
    	return NEW_STRUCT(func,a,new struct_a { RAW_STRING(s) });
    %}

    FUNCTION func.d INT -> VARIABLE
    %{
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal(svm,z,AUTOMATIC,i);
    	return z;
    %}

    INSTRUCTION func.example
    %{
    	SVM_Value_String d = ::svm_value_string_new__raw(svm,"default value");
    	SVM_Parameter *parameters = ::svm_parameter_array_new(svm,2);
    	parameters[0] = ::svm_parameter_value_new(svm,d);
    	SVM_Structure s = ::svm_function_call(svm,CONST_PEP(func,c),1,parameters);
    	parameters[0] = ::svm_parameter_structure_new(svm,s);
    	parameters[1] = ::svm_parameter_variable_new(svm,CURRENT(kernel));
    	::svm_function_call(svm,CONST_PEP(func,b),2,parameters);
    	auto rs = reinterpret_cast<struct_a*>(::svm_structure_get_internal(svm,CONST_PEP(func,a),s));
    	std::cout << "###" << std::endl << rs->_s << "###" << std::endl;
    %}

Cette fois, la structure modifiée est créée via un appel de fonction (ce qui permet de la créer depuis une autre extension !). Le tableau de paramètres peut, comme il est montré dans cet exemple, être réutilisé tant que le nombre de paramètres effectifs de la fonction ne dépasse pas la taille du tableau.

La structure retournée est quant à elle directement récupérée en tant que valeur de retour de la fonction d'interface programmatique.

Générez et compilez l'extension. Relancez directement votre application de test : le comportement ne devrait pas changer.

- Un appel de fonction extension se réalise par la fonction d'interface programmatique `svm_function_call`, où le nom de la fonction est un point d'entrée d'extension. Cela permet à une extension d'appeler une fonction extension de n'importe quel autre extension : ce mécanisme est le point de départ de la collaboration entre extensions.
- Les paramètres de fonctions sont donnés dans un tableau de paramètres créé avec les fonctions `svm_parameter_array_new` et rempli avec les fonctions `svm_parameter_type_new`.
- Une éventuelle valeur de retour est directement retournée par la fonction `svm_function_call`.

# Usages

Comme mentionné précédemment, les fonctions sont la porte d'entrée de la collaboration entre extensions. Et nous allons ici voir sur quelques exemples (non exhaustifs) comment peut s'implémenter une telle collaboration.

## Réutilisation

Cette utilisation est la plus basique de toute : il s'agit de reproduire dans les extensions ce que les fonctions du C ou du C++ font. Il s'agit d'éclater le traitement d'une extension en plusieurs morceaux, ces morceaux pouvant rester privés (en étant définis en tant que fonctions C/C++ dans l'extension) ou mis à disposition des autres extensions (en étant définis comme fonctions d'extension).

Les développeurs ayant largement l'habitude de ce mécanisme, il ne sera pas présenté ici.

## Isolation mémoire des types et structures

Les structures semblaient dans le didacticiel les présentant relativement inutiles. De même, les types extensions semblaient cantonnés à leur extension. Il manquait clairement quelque chose pour les rendre manipulable depuis d'autres extensions. Ce manque est comblé par les fonctions d'extension : autant les types et structures ont une partie opaque aux autres extensions, autant les fonctions n'ont pas ce défaut. Et les fonctions peuvent manipuler les types et structures !

Modifiez le code de l'extension :

    PLUGIN func

    DEFINE

    STRUCT func.s
    %{
    	std::string _a;
    	std::string _b;
    %}
    delete default: %{}
    copy default: %{}

    FUNCTION func.new STR 2 -> $func.s
    %{
    	auto a = ARGV_VALUE(0,string);
    	auto b = ARGV_VALUE(1,string);
    	return NEW_STRUCT(func,s,(new struct_s { RAW_STRING(a), RAW_STRING(b) }));
    %}

    FUNCTION func.swap MUTABLE $func.s
    %{
    	auto s = ARGV_STRUCT(0,func,s);
    	std::swap(s->_a,s->_b);
    %}

    FUNCTION func.push MUTABLE $func.s STR -> STR
    %{
    	auto s = ARGV_STRUCT(0,func,s);
    	auto f = ARGV_VALUE(1,string);
    	auto r = s->_b;
    	s->_b = s->_a;
    	s->_a = RAW_STRING(f);
    	return NEW_VALUE(string,NEW_STRING(r));
    %}

    FUNCTION func.write $func.s VARIABLE -> PTR
    %{
    	auto s = ARGV_STRUCT(0,func,s);
    	SVM_Kernel k = ARGV_VARIABLE(1);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,STRING,2);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,k,z);
    	SVM_Value *v = ::svm_value_array_new(svm,2);
    	v[0] = ::svm_value_string_new__buffer(svm,s->_a.c_str(),s->_a.size());
    	v[1] = ::svm_value_string_new__buffer(svm,s->_b.c_str(),s->_b.size());
    	::svm_memory_write_pointer(svm,k,p,v);
    	return p;
    %}

Générez et compilez l'extension. Maintenant, ouvrez un autre fichier d'extension, et ajoutez-y ce code :

    PLUGIN other

    code:
    %{
    	static SVM_Structure _s = nullptr;
    %}

    initialisation:
    %{
    	SVM_Parameter *p = ::svm_parameter_array_new(svm,2);
    	p[0] = ::svm_parameter_value_new(svm,::svm_value_string_new__raw(svm,""));
    	p[1] = ::svm_parameter_value_new(svm,::svm_value_string_new__raw(svm,""));
    	_s = ::svm_function_call(svm,CONST_PEP(func,new),2,p);
    	VARIABLE_GLOBAL(_s);
    %}

    finalisation:
    %{
    	VARIABLE_LOCAL(_s);
    %}

    USE

    STRUCT func.s

    FUNCTION func.new STR STR -> $func.s

    FUNCTION func.push MUTABLE $func.s STR -> STR

    FUNCTION func.write $func.s VARIABLE -> PTR

    DEFINE

    INSTRUCTION other.add STR -> STR
    %{
    	SVM_Parameter *p = ::svm_parameter_array_new(svm,2);
    	p[0] = ::svm_parameter_structure_new(svm,_s);
    	p[1] = argv[0];
    	return ::svm_function_call(svm,CONST_PEP(func,push),2,p);
    %}

    INSTRUCTION other.snapshot -> PTR
    %{
    	SVM_Parameter *p = ::svm_parameter_array_new(svm,2);
    	p[0] = ::svm_parameter_structure_new(svm,_s);
    	p[1] = ::svm_parameter_variable_new(svm,CURRENT(kernel));
    	return ::svm_function_call(svm,CONST_PEP(func,write),2,p);
    %}

Générez et compilez l'extension. Maintenant, écrivez une petite application utilisant les deux instructions `:other.add` et `:other.snapshot`. Ici, l'extension other a pu manipuler la structure au travers de fonction sans avoir à connaître la structure C/C++ sous-jacente, ce qui garantit une dépendance faible d'implémentation entre les deux extensions.

## Polymorphisme inter-extension

Une forme encore plus aboutie de collaboration entre extensions est le polymorphisme inter-extension. Il s'agit ici de faire travailler plusieurs extensions ensemble sans qu'elles le sachent : c'est le code machine qui va orchestrer l'emploi de plusieurs extensions de manière transparente.

Modifiez le code de l'extension initiale :

    PLUGIN func

    DEFINE

    TYPE func.t
    %{
    	SVM_Value_PluginEntryPoint _type;
    	SVM_Structure _s;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_type);
    	VARIABLE_LOCAL(object->_s);
    %}

    INSTRUCTION func.new PEP = VALUE * -> func.t
    %{
    	SVM_Value_PluginEntryPoint type = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_shared(svm,type);
    	VARIABLE_GLOBAL(type);
    	SVM_Structure s = ::svm_function_call(svm,::svm_value_pluginentrypoint_new_prefix(svm,type,"new"),argc-2,argv+2);
    	VARIABLE_GLOBAL(s);
    	return NEW_PLUGIN(func,t,(new type_t { type, s }));
    %}

    INSTRUCTION func.apply func.t PEP = VALUE * -> func.t
    %{
    	auto t = ARGV_PLUGIN(0,func,t);
    	argv[0] = ::svm_parameter_structure_new(svm,t->_s);
    	SVM_Structure s = ::svm_function_call(svm,::svm_value_pluginentrypoint_new_prefix(svm,t->_type,"apply"),argc,argv);
    	VARIABLE_GLOBAL(t->_type);
    	VARIABLE_GLOBAL(s);
    	return NEW_PLUGIN(func,t,(new type_t { t->_type, s }));
    %}

    INSTRUCTION func.accumulate func.t PEP = VALUE * -> VALUE
    %{
    	auto t = ARGV_PLUGIN(0,func,t);
    	argv[0] = ::svm_parameter_structure_new(svm,t->_s);
    	return ::svm_function_call(svm,::svm_value_pluginentrypoint_new_prefix(svm,t->_type,"accumulate"),argc,argv);
    %}

    FUNCTION func.inc INT -> INT
    %{
    	auto i = ARGV_VALUE(0,integer);
    	return NEW_VALUE(integer,i+1);
    %}

    FUNCTION func.dec INT -> INT
    %{
    	auto i = ARGV_VALUE(0,integer);
    	return NEW_VALUE(integer,i-1);
    %}

    FUNCTION func.add INT INT -> INT
    %{
    	auto a = ARGV_VALUE(0,integer);
    	auto i = ARGV_VALUE(1,integer);
    	return NEW_VALUE(integer,a+i);
    %}

L'extension maintenant définit un type ultra-générique, dont la construction par l'instruction `:func.new` indique l'implementation à utiliser. Ensuite, elle définit deux fonctions qui vont appliquer un traitement à la structure d'implémentation : `:func.apply` et `:func.accumulate`. Les dernières fonctions sont de simples opérations encapsulées dans des fonctions extension.

Notez dans le code de l'extension la présence de la fonction `svm_value_pluginentrypoint_new_prefix`, qui permet de construire le nom des fonctions à appeler à partir du nom du type : par exemple, le type `plugin.entry` devient le nom de fonction `plugin.new_entry` pour la création de la structure.

Il reste maintenant à fournir des implémentations pour la structure fille du type. Modifiez le code de la seconde extension "other" avec ce code :

    PLUGIN other

    includes:
    %{
    #include <vector>
    %}

    DEFINE

    STRUCT other.intarray
    %{
    	std::vector<SVM_Value_Integer> _v;
    %}
    delete default:
    %{
    	for(auto& i: object->_v) { VARIABLE_LOCAL(i); }
    %}

    FUNCTION other.new_intarray INT * -> $other.intarray
    %{
    	auto v = new struct_intarray;
    	for(SVM_Size i=0 ; i<argc ; ++i)
    	{
    		SVM_Value_Integer ai = ::svm_parameter_value_get(svm,argv[i]);
    		VARIABLE_GLOBAL(ai);
    		v->_v.push_back(ai);
    	}
    	return NEW_STRUCT(other,intarray,v);
    %}

    FUNCTION other.apply_intarray $other.intarray PEP = VALUE * -> $other.intarray
    %{
    	auto s = ARGV_STRUCT(0,other,intarray);
    	SVM_Value_PluginEntryPoint func = ::svm_parameter_value_get(svm,argv[1]);
    	auto rv = new struct_intarray;
    	for(const auto& v:s->_v)
    	{
    		argv[2] = ::svm_parameter_value_new(svm,v);
    		SVM_Value_Integer i = ::svm_function_call(svm,func,argc-2,argv+2);
    		VARIABLE_GLOBAL(i);
    		rv->_v.push_back(i);
    	}
    	return NEW_STRUCT(other,intarray,rv);
    %}

    FUNCTION other.accumulate_intarray $other.intarray PEP = VALUE * -> INT
    %{
    	auto s = ARGV_STRUCT(0,other,intarray);
    	SVM_Value_PluginEntryPoint func = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer a = NEW_VALUE(integer,0);
    	for(const auto& v:s->_v)
    	{
    		argv[1] = ::svm_parameter_value_new(svm,a);
    		argv[2] = ::svm_parameter_value_new(svm,v);
    		a = ::svm_function_call(svm,func,argc-1,argv+1);
    	}
    	return a;
    %}

    STRUCT other.stringarray
    %{
    	std::vector<SVM_Value_String> _v;
    %}
    delete default:
    %{
    	for(auto& i: object->_v) { VARIABLE_LOCAL(i); }
    %}

    FUNCTION other.new_stringarray STR * -> $other.stringarray
    %{
    	auto v = new struct_stringarray;
    	for(SVM_Size i=0 ; i<argc ; ++i)
    	{
    		SVM_Value_String as = ::svm_parameter_value_get(svm,argv[i]);
    		VARIABLE_GLOBAL(as);
    		v->_v.push_back(as);
    	}
    	return NEW_STRUCT(other,stringarray,v);
    %}

    FUNCTION other.apply_stringarray $other.stringarray PEP = VALUE * -> $other.stringarray
    %{
    	auto s = ARGV_STRUCT(0,other,stringarray);
    	SVM_Value_PluginEntryPoint func = ::svm_parameter_value_get(svm,argv[1]);
    	auto rv = new struct_stringarray;
    	for(const auto& v:s->_v)
    	{
    		argv[2] = ::svm_parameter_value_new(svm,v);
    		SVM_Value_String i = ::svm_function_call(svm,func,argc-2,argv+2);
    		VARIABLE_GLOBAL(i);
    		rv->_v.push_back(i);
    	}
    	return NEW_STRUCT(other,stringarray,rv);
    %}

    FUNCTION other.accumulate_stringarray $other.stringarray PEP = VALUE * -> STR
    %{
    	auto s = ARGV_STRUCT(0,other,stringarray);
    	SVM_Value_PluginEntryPoint func = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_String a = ::svm_value_string_new__raw(svm,"");
    	for(const auto& v:s->_v)
    	{
    		argv[1] = ::svm_parameter_value_new(svm,a);
    		argv[2] = ::svm_parameter_value_new(svm,v);
    		a = ::svm_function_call(svm,func,argc-1,argv+1);
    	}
    	return a;
    %}

    FUNCTION other.concat STR STR -> STR
    %{
    	auto l = ARGV_VALUE(0,string);
    	auto r = ARGV_VALUE(1,string);
    	std::string rs = RAW_STRING(l)+RAW_STRING(r);
    	return NEW_VALUE(string,NEW_STRING(rs));
    %}

Cette extension est en trois parties : les deux premières fournissent chacune une implémentation pour le type `func.t`, et la dernière ajoute une fonction de concaténation de chaines de caractères.

Générez et compilez les deux extensions. Puis, ouvrez un fichier d'application et ajoutez-y ce code :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginfunc/libsvmfunc.so"
    LOCAL PLUGIN "svmpluginother/libsvmother.so"
    PLUGIN "svmcom.so"
    PROCESS "main"
    	CODE "main" INLINE
    		:memory func.t/t, func.t/tt, INT/i, STR/s
    		:func.new other.intarray = 1 2 3 4 5 -> &t
    		:func.apply @&t func.inc = -> &tt
    		:func.accumulate @&tt func.add = -> &i
    		:com.message @&i
    		:func.new other.stringarray = "a" "b" "c" "d" "e" -> &t
    		:func.apply @&t other.concat = "_1 " -> &tt
    		:func.accumulate @&tt other.concat = -> &s
    		:com.message @&s
    	END
    END

Lancez l'application :

    ./functions.svm
    20
    a_1 b_1 c_1 d_1 e_1

Les deux instructions de modification du type arrivent à travailler de manière transparente :

1. sur le type des données traitées et leur stockage,
2. sur les opérations qui sont appliquées aux valeurs à l'intérieur des structures.

Et tout cela, au sein des mêmes instructions, le tout contrôlé depuis le code machine. Notez que le comportement présenté ici est exactement celui qu'adopte le type `com.device` pour se comporter comme une interface de périphérique standardisée.

- Les fonctions peuvent servir à factoriser du code (utilisation banale).
- Les fonctions permettent également d'étendre la gestion d'un type ou d'une structure à d'autres extensions par le truchement d'appels de fonctions.
- Les fonctions permettent d'implémenter un polymorphisme dynamique sur les types et les structures, dont la sélection de la classe fille peut être faite dans le code de la machine.

# Conclusion

Vous venez de voir comment créer et manipuler les fonctions extensions depuis une extension.

Les fonctions apportent énormément de capacités aux extensions en leur permettant de travailler ensemble sans forcément avoir de dépendance forte entre elles.

Elles redonnent aussi un sens à l'existence des structures extensions, qui participent à leur manière à cette collaboration.

Pour un concepteur d'extension, une maîtrise approfondie des fonctions extension peut donner une véritable généricité à son extension, sans pour autant sacrifier sa simplicité d'utilisation.