Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question de généricité

21 views
Skip to first unread message

DrPi

unread,
Aug 29, 2023, 5:27:47 PM8/29/23
to
Bonjour,

Une question sur un problème de généricité.

J'ai les types suivants :

subtype t_TW_STR32 is char_array (1 .. 34);
subtype t_TW_STR64 is char_array (1 .. 66);
subtype t_TW_STR128 is char_array (1 .. 130);
subtype t_TW_STR255 is char_array (1 .. 256);

char_array vient de la bibliothèque standard Interfaces.C.
Sa définition est :

type char_array is array (size_t range <>) of aliased char;

J'ai créé une fonction de conversion générique comme suit :

generic
Char_Array_Length : Natural;
function To_TW_STR (Item : String) return char_array;

function To_TW_STR (Item : String) return char_array is
begin
return Result : char_array (0 .. size_t(Char_Array_Length - 1))
with Relaxed_Initialization do
for J in Item'Range loop
Result (size_t (J - Item'First)) := To_C (Item (J));
end loop;
Result (size_t(Item'Last + 1)) := nul;
end return;
end To_TW_STR;

Et je crée des instanciations comme ceci :

function To_TW_STR32 is new To_TW_STR (t_TW_STR32'Length);
function To_TW_STR64 is new To_TW_STR (t_TW_STR64'Length);
function To_TW_STR128 is new To_TW_STR (t_TW_STR128'Length);
function To_TW_STR255 is new To_TW_STR (t_TW_STR255'Length);


Jusque là, tout va bien. Cela fonctionne très bien pour mon usage.

Pour ma connaissance, j'aimerais créer une fonction "plus générique".
C'est à dire, passer un type générique au lieu de coder en dur le type
retourné.
Quelque chose comme ceci :

generic
type T is private; -- private ???
function To_TW_STR (Item : String) return T;

function To_TW_STR (Item : String) return T is
begin
return Result : T do
for J in Item'Range loop
Result (size_t (J - Item'First)) := To_C (Item (J));
end loop;
Result (size_t(Item'Last + 1)) := nul;
end return;
end To_TW_STR;

function To_TW_STR32 is new To_TW_STR (t_TW_STR32);
function To_TW_STR64 is new To_TW_STR (t_TW_STR64);
function To_TW_STR128 is new To_TW_STR (t_TW_STR128);
function To_TW_STR255 is new To_TW_STR (t_TW_STR255);


Mais le compilateur n'aime pas.

Il me semble qu'une écriture possible est la suivante :

generic
type Item_Type is private;
type Index_Type is (<>);
type Array_Type is array (Index_Type) of Item_Type;
function To_TW_STR (Item : String) return Array_Type;

function To_TW_STR (Item : String) return Array_Type is
begin
return Result : Array_Type with Relaxed_Initialization do
for J in Item'Range loop
Result (size_t (J - Item'First)) := To_C (Item (J));
end loop;
Result (size_t(Item'Last + 1)) := nul;
end return;
end To_TW_STR;

Mais je n'arrive pas l'instancier.

Comment faire ?

Nicolas

Gautier write-only address

unread,
Aug 29, 2023, 9:16:04 PM8/29/23
to
J'ai ajouté qqes paramètres formels:
with Interfaces.C;

procedure genconv is

use Interfaces.C;

type char_array is array (size_t range <>) of aliased char;

-- subtype t_TW_STR32 is char_array (1 .. 34);
-- subtype t_TW_STR64 is char_array (1 .. 66);
-- subtype t_TW_STR128 is char_array (1 .. 130);
-- subtype t_TW_STR255 is char_array (1 .. 256);

generic
type Item_Type is private;
nul : Item_Type;
type Index_Type is mod <>;
type Array_Type is array (Index_Type range <>) of Item_Type;
with function To_Item_Type (c : Character) return Item_Type;
function To_TW_STR (Item : String) return Array_Type;

function To_TW_STR (Item : String) return Array_Type is
begin
return Result : Array_Type (1 .. Item'Length + 1) with Relaxed_Initialization do
for J in Item'Range loop
Result (Index_Type (J - Item'First + 1)) := To_Item_Type (Item (J)); -- Was J - Item'First
end loop;
Result (Index_Type (Item'Length + 1)) := nul; -- Was Item'Last + 1
end return;
end To_TW_STR;

function To_TW_C is new To_TW_STR (char, nul, size_t, char_array, to_C);

begin
null;
end;

Variante: l'indexation de ton résultat commence par 0:
...
return Result : Array_Type (0 .. Item'Length) with Relaxed_Initialization do
for J in Item'Range loop
Result (Index_Type (J - Item'First)) := To_Item_Type (Item (J));
end loop;
Result (Index_Type (Item'Length)) := nul;
end return;

DrPi

unread,
Aug 30, 2023, 4:14:01 PM8/30/23
to
> -- subtype t_TW_STR32 is char_array (1 .. 34);
> -- subtype t_TW_STR64 is char_array (1 .. 66);
> -- subtype t_TW_STR128 is char_array (1 .. 130);
> -- subtype t_TW_STR255 is char_array (1 .. 256);
>
> generic
> type Item_Type is private;
> nul : Item_Type;
> type Index_Type is mod <>;
> type Array_Type is array (Index_Type range <>) of Item_Type;
> with function To_Item_Type (c : Character) return Item_Type;
> function To_TW_STR (Item : String) return Array_Type;
>
> function To_TW_STR (Item : String) return Array_Type is
> begin
> return Result : Array_Type (1 .. Item'Length + 1) with Relaxed_Initialization do
> for J in Item'Range loop
> Result (Index_Type (J - Item'First + 1)) := To_Item_Type (Item (J)); -- Was J - Item'First
> end loop;
> Result (Index_Type (Item'Length + 1)) := nul; -- Was Item'Last + 1
> end return;
> end To_TW_STR;
>
> function To_TW_C is new To_TW_STR (char, nul, size_t, char_array, to_C);
>
> begin
> null;
> end;
>

Je ne comprends pas "nul : Item_Type" dans les paramètres génériques. A
quoi sert cette ligne ?

Je ne comprends pas non plus pourquoi on passe "Item_Type" et
"Index_Type" sachant que "Array_Type" définit complètement le type
("char_array" ici).

Dans mon cas, je veux passer un type entièrement contraint en générique.
La taille du tableau de sortie doit être égale au type entièrement
contraint passé en générique, pas au paramètre "Item" de la fonction.
Comment faire ?

Sinon, il faudrait avoir accès à l'élément assigné (LHS) à partir de la
fonction.
En VHDL-2019 (langage très fortement inspiré de Ada), une fonction peut
s'écrire :
function my_func (param1 : in t_type1; param2 : in t_type2) return
param_ret of t_type_ret;

Par exemple avec "var := my_func (var1, var2);"
la fonction "my_func" a accès à "var" à travers l'identifiant
"param_ret". Dans "my_func", il est possible d'utiliser
"param_ret'Length" ou "param_ret'Range" par exemple.
Je ne crois pas que ce soit possible en Ada. Exact ?


Gautier write-only address

unread,
Aug 30, 2023, 4:59:00 PM8/30/23
to
> Je ne comprends pas "nul : Item_Type" dans les paramètres génériques. A
> quoi sert cette ligne ?
Enlève-la et essaie de compiler!

> Je ne comprends pas non plus pourquoi on passe "Item_Type" et
> "Index_Type" sachant que "Array_Type" définit complètement le type
> ("char_array" ici).
Pareil. Mais dans une version hypothétique d'Ada où tu ne passerais que le type tableau, il faudrait un attribut pour extraire le type des éléments et le type de l'indice (tu as besoin des deux dans ton code).

DrPi

unread,
Aug 31, 2023, 2:13:09 PM8/31/23
to
Le 30/08/2023 à 22:58, Gautier write-only address a écrit :
>> Je ne comprends pas "nul : Item_Type" dans les paramètres génériques. A
>> quoi sert cette ligne ?
> Enlève-la et essaie de compiler!
Oui, ça compile pas. Mais ça ne répond pas à ma question ;)

>
>> Je ne comprends pas non plus pourquoi on passe "Item_Type" et
>> "Index_Type" sachant que "Array_Type" définit complètement le type
>> ("char_array" ici).
> Pareil. Mais dans une version hypothétique d'Ada où tu ne passerais que le type tableau, il faudrait un attribut pour extraire le type des éléments et le type de l'indice (tu as besoin des deux dans ton code).
C'est justement ce à quoi je pensais.
Une amélioration à intégrer à une future version de Ada :)


Et du coup, est-il possible de contraindre Array_Type ?

Gautier write-only address

unread,
Aug 31, 2023, 6:18:50 PM8/31/23
to
> >> Je ne comprends pas "nul : Item_Type" dans les paramètres génériques. A
> >> quoi sert cette ligne ?
> > Enlève-la et essaie de compiler!
> Oui, ça compile pas. Mais ça ne répond pas à ma question ;)

Sans cette ligne, le compilateur ne sait pas du tout ce que peut signifier ce truc qui s'appelle "nul", là où il y a ":= nul;"
Évidemment, dans l'instanciation particulière que tu as en tête, ce "nul" est bien un octet de valeur 0, et même provenant de Interfaces.C, surtout qu'il porte par hasard le même nom.
Mais c'est une coïncidence et ton intuition fait son travail.
Tu pourrais avoir dans le code générique "terminator" au lieu de "nul".
Result (Index_Type (Item'Length + 1)) := terminator;
Dans ce cas, que signifie ou vaut "terminator"? Valeur ? Fonction?...

Tu peux avoir n'importe quel type comme Item_Type.
Un exemple simple:
type U32 is mod 2**32;

type Float_Array is array (U32 range <>) of aliased Float;

function To_Float (c : Character) return Float is (Float (Character'Pos (c)));

function To_TW_Float is new To_TW_STR (Float, 3.1415927, U32, Float_Array, To_Float);

Tu peux avoir qqch d'un peu plus compliqué:
type My_Item is record a, b, c : Float; d: Positive; end record;

DrPi

unread,
Sep 1, 2023, 10:52:04 AM9/1/23
to
Le 01/09/2023 à 00:18, Gautier write-only address a écrit :
>>>> Je ne comprends pas "nul : Item_Type" dans les paramètres génériques. A
>>>> quoi sert cette ligne ?
>>> Enlève-la et essaie de compiler!
>> Oui, ça compile pas. Mais ça ne répond pas à ma question ;)
>
> Sans cette ligne, le compilateur ne sait pas du tout ce que peut signifier ce truc qui s'appelle "nul", là où il y a ":= nul;"
> Évidemment, dans l'instanciation particulière que tu as en tête, ce "nul" est bien un octet de valeur 0, et même provenant de Interfaces.C, surtout qu'il porte par hasard le même nom.
> Mais c'est une coïncidence et ton intuition fait son travail.
> Tu pourrais avoir dans le code générique "terminator" au lieu de "nul".
> Result (Index_Type (Item'Length + 1)) := terminator;
> Dans ce cas, que signifie ou vaut "terminator"? Valeur ? Fonction?...
>
Ha, voilà, c'est le "nul" qui m'a perturbé. J'ai confondu avec "null".
Des fois, il faut pas grand chose pour comprendre de travers ;)

> Tu peux avoir n'importe quel type comme Item_Type.
> Un exemple simple:
> type U32 is mod 2**32;
>
> type Float_Array is array (U32 range <>) of aliased Float;
>
> function To_Float (c : Character) return Float is (Float (Character'Pos (c)));
>
> function To_TW_Float is new To_TW_STR (Float, 3.1415927, U32, Float_Array, To_Float);
>
> Tu peux avoir qqch d'un peu plus compliqué:
> type My_Item is record a, b, c : Float; d: Positive; end record;

C'est pas le ItemType qui me pose problème.

Ce qui me pose problème, c'est que j'aimerais que la fonction
"To_TW_STR" renvoie un type entièrement contraint.
EN reprenant les définitions de départ /
subtype t_TW_STR32 is char_array (1 .. 34);
subtype t_TW_STR64 is char_array (1 .. 66);
subtype t_TW_STR128 is char_array (1 .. 130);
subtype t_TW_STR255 is char_array (1 .. 256);

J'aimerais pouvoir instancier une fonction qui retourne "t_TW_STR32",
une qui retourne "t_TW_STR64"...

Gautier write-only address

unread,
Sep 1, 2023, 2:42:41 PM9/1/23
to
Que dis-tu du code ci-dessous?
Une question à mon tour: pourquoi le choix 32, 64, 128 (puissances de deux) puis 255 (=2^8 - 1) et pourquoi +2 pour la longueur des trois premiers tableaux et +1 pour le dernier?

----8<-----------8<-----------8<-----------8<-----------8<-----------8<-------
with Interfaces.C;

procedure genconv is

generic
type Item_Type is private;
terminator : Item_Type;
type Index_Type is mod <>;
array_length : Index_Type;
type Array_Type is array (Index_Type range <>) of Item_Type;
with function To_Item_Type (c : Character) return Item_Type;

package TW_Factory is
subtype Tailored_Array is Array_Type (Index_Type'(1) .. array_length);
function To_TW_STR (message : String) return Tailored_Array;
end TW_Factory;

package body TW_Factory is
function To_TW_STR (message : String) return Tailored_Array is
begin
pragma Assert (message'Length = array_length); -- Or adjust terminator position ?
return Result : Tailored_Array with Relaxed_Initialization do
for J in message'Range loop
Result (Index_Type (J - message'First + 1)) := To_Item_Type (message (J));
end loop;
Result (Index_Type (message'Length + 1)) := terminator;
end return;
end To_TW_STR;
end TW_Factory;

use Interfaces.C;

type char_array is array (size_t range <>) of aliased char;

package TW_128 is new TW_Factory (char, nul, size_t, 128, char_array, to_C);

x : TW_128.Tailored_Array;

begin
x := TW_128.To_TW_STR ("Hey!");
end;

J-P. Rosen

unread,
Sep 2, 2023, 3:18:08 AM9/2/23
to
Le 01/09/2023 à 16:52, DrPi a écrit :
> Ce qui me pose problème, c'est que j'aimerais que la fonction
> "To_TW_STR" renvoie un type entièrement contraint.
> EN reprenant les définitions de départ /
>    subtype t_TW_STR32  is char_array (1 ..  34);
>    subtype t_TW_STR64  is char_array (1 ..  66);
>    subtype t_TW_STR128 is char_array (1 .. 130);
>    subtype t_TW_STR255 is char_array (1 .. 256);
>
> J'aimerais pouvoir instancier une fonction qui retourne "t_TW_STR32",
> une qui retourne "t_TW_STR64"...

En Ada, l'homonymie et la résolution des surcharges est basée sur les
types, jamais sur les sous-types. Pourquoi? Parce que le type est connu
à la compilation, mais qu'un sous-type peut être dynamique.

--
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
https://www.adalog.fr https://www.adacontrol.fr

DrPi

unread,
Sep 2, 2023, 3:53:05 AM9/2/23
to
Le 01/09/2023 à 20:42, Gautier write-only address a écrit :
> Que dis-tu du code ci-dessous?
J'y ait pensé mais j'espérais une écriture plus "intelligente".

> Une question à mon tour: pourquoi le choix 32, 64, 128 (puissances de deux) puis 255 (=2^8 - 1) et pourquoi +2 pour la longueur des trois premiers tableaux et +1 pour le dernier?
>
Je ne sais pas :p
J'essaie de m'interfacer sur une DLL dont le fichier d'entête associé
définit les choses comme ça.

// String types. These include room for the strings and a NULL char,
// or, on the Mac, a length byte followed by the string.
// TW_STR255 must hold less than 256 chars so length fits in first byte.
#if defined(__APPLE__)-- cf: Mac version of TWAIN.h
typedef unsigned char TW_STR32[34], FAR *pTW_STR32;
typedef unsigned char TW_STR64[66], FAR *pTW_STR64;
typedef unsigned char TW_STR128[130], FAR *pTW_STR128;
typedef unsigned char TW_STR255[256], FAR *pTW_STR255;
#else
typedef char TW_STR32[34], FAR *pTW_STR32;
typedef char TW_STR64[66], FAR *pTW_STR64;
typedef char TW_STR128[130], FAR *pTW_STR128;
typedef char TW_STR255[256], FAR *pTW_STR255;
#endif

DrPi

unread,
Sep 2, 2023, 6:20:17 AM9/2/23
to
Le 02/09/2023 à 09:18, J-P. Rosen a écrit :
> Le 01/09/2023 à 16:52, DrPi a écrit :
>> Ce qui me pose problème, c'est que j'aimerais que la fonction
>> "To_TW_STR" renvoie un type entièrement contraint.
>> EN reprenant les définitions de départ /
>>     subtype t_TW_STR32  is char_array (1 ..  34);
>>     subtype t_TW_STR64  is char_array (1 ..  66);
>>     subtype t_TW_STR128 is char_array (1 .. 130);
>>     subtype t_TW_STR255 is char_array (1 .. 256);
>>
>> J'aimerais pouvoir instancier une fonction qui retourne "t_TW_STR32",
>> une qui retourne "t_TW_STR64"...
>
> En Ada, l'homonymie et la résolution des surcharges est basée sur les
> types, jamais sur les sous-types. Pourquoi? Parce que le type est connu
> à la compilation, mais qu'un sous-type peut être dynamique.
>
OK.
Donc, si je changeais "t_TW_STR32" et autres en "type" et non en
"subtype", il serait possible de faire ce que je veux ?

DrPi

unread,
Sep 2, 2023, 8:34:40 AM9/2/23
to
>> Une question à mon tour: pourquoi le choix 32, 64, 128 (puissances de
>> deux) puis 255 (=2^8 - 1) et pourquoi +2 pour la longueur des trois
>> premiers tableaux et +1 pour le dernier?
>>
> Je ne sais pas :p
> J'essaie de m'interfacer sur une DLL dont le fichier d'entête associé
> définit les choses comme ça.
>
> //  String types. These include room for the strings and a NULL char,
> //  or, on the Mac, a length byte followed by the string.
> //  TW_STR255 must hold less than 256 chars so length fits in first byte.
> #if defined(__APPLE__)--   cf: Mac version of TWAIN.h
>     typedef unsigned char TW_STR32[34],     FAR *pTW_STR32;
>     typedef unsigned char TW_STR64[66],     FAR *pTW_STR64;
>     typedef unsigned char TW_STR128[130],   FAR *pTW_STR128;
>     typedef unsigned char TW_STR255[256],   FAR *pTW_STR255;
> #else
>     typedef char          TW_STR32[34],     FAR *pTW_STR32;
>     typedef char          TW_STR64[66],     FAR *pTW_STR64;
>     typedef char          TW_STR128[130],   FAR *pTW_STR128;
>     typedef char          TW_STR255[256],   FAR *pTW_STR255;
> #endif
>

Je pense que j'ai trouvé la réponse.
Les types TW_STRXXX sont utilisés dans des structures encapsulées dans
une directive "#pragma pack (2)".
Donc TW_STR32 a 32 octets pour les caractères + 1 octet pour le zéro de
fin de chaîne + 1 octet d'alignement.
Pareil pour les autres. Sauf TW_STR255 qui n'a pas besoin d'octet
d'alignement.
En théorie, il n'y a pas besoin de cet octet d'alignement puisque la
directive "pack" est là pour ça. Mais peut-être qu'en pratique, avec
certains compilateurs, il faut l'ajouter.

Thomas

unread,
Sep 15, 2023, 5:49:55 PM9/15/23
to
je serais curieux d'avoir la fin de ton retour d’expérience par rapport
à ce pb, cad :
- savoir si ça a résolu ton pb,
- et surtout savoir si ça n'en a pas généré d'autres à des endroits où
tu avais besoin que ces sous-types soient compatibles entre eux :-)


--
RAPID maintainer
http://savannah.nongnu.org/projects/rapid/

DrPi

unread,
Sep 16, 2023, 5:05:32 AM9/16/23
to
J'avoue que je n'ai pas essayé d'aller plus loin.
C'était une question théorique, pour ma connaissance. La réponse, très
instructive, est beaucoup plus complexe que ce que j'imaginais. Ada est
un langage très intéressant mais pas toujours facile à appréhender.
Je me concentre sur mon projet en cours.
0 new messages