Reputation: 55
I am trying to convert a set of Bytes to an enumerated set using Generics. But the code does not compile. TValue.FromOrdinal(TypeInfo(T), Ord(B)).AsType does actually correctly return the enumerated value but I cannot include this value in the enumerated set.
interface
type TByteSet = set of Byte;
type TMyNewEnum = (meZero, meOne, meTwo);
type TMyNewEnumSet = set of TMyNewEnum;
type
TEnum<T> = class(TObject)
public
class function ToString(const aEnumValue: T): string; reintroduce;
class function FromString(const aEnumString: string; const aDefault: T): T;
class procedure FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
end
implementation
Var
MyByteSet: TMyByteSet;
MyEnumSet: TMyNewEnumSet;
...
class procedure TEnum<T>.FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
var
B: Byte;
begin
Assert(PTypeInfo(TypeInfo(T)).Kind = tkEnumeration, 'Type parameter must be an Enumeration');
for B in Value do
begin
EnumSet := EnumSet + TValue.FromOrdinal(TypeInfo(T), Ord(B)).AsType<T>; //This line does not compile
end;
end;
...
//intended Usage
MyByteSet := [0, 2];
TEnum<TMyNewEnum>.FromByteSet(MyByteSet, MyEnumSet);
//I would like MyEnumSet to contain [meZero, meTwo]
end.
Any ideas?
Upvotes: 2
Views: 575
Reputation: 6013
You can achieve what you want quite simply, but not the way you are trying to do it (which has been pointed out by others)
If you step through the following program you will see in the debugger that MyEnumSet ends up with the desired value.
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type TByteSet = set of Byte;
type TMyNewEnum = (meZero, meOne, meTwo);
type TMyNewEnumSet = set of TMyNewEnum;
type
TEnum<T> = class(TObject)
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: T);
end;
Var
MyByteSet: TByteSet;
MyEnumSet: TMyNewEnumSet;
procedure Test( const Parm1 : TByteSet; out Parm2 : TMyNewEnumSet );
var
iResult : TMyNewEnumSet absolute Parm1;
begin
Parm2 := iResult;
end;
{ TEnum<T> }
class procedure TEnum<T>.FromByteSet(const Value: TByteSet; out EnumSet : T );
var
iResult : T absolute Value;
begin
EnumSet := iResult;
end;
begin
MyByteSet := [0,2];
TEnum<TMyNewEnumSet>.FromByteSet( MyByteSet, MyEnumSet);
end.
Of course, you will need to add error checking (bounds and so on) for which you can use RTTI.
Upvotes: -1
Reputation: 613003
What you are attempting is not possible. For it to be possible, you would need to be able to constrain the generic type parameter to be a type over which a set can be formed. But no such generic constraint is supported by the language.
In fact your existing code already contains the tell-tale signs of the root problem. You have:
type
TEnum<T> = class(TObject)
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
end;
The elephant in the room here is that FromByteSet
makes no reference to T
and so is not generic.
In order to make the function generic you would need something like this:
type
TEnum<T: record> = class(TObject)
private
type SetOfT = set of T;
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: SetOfT);
end;
This does not compile. The compiler objects to the type declaration with:
[dcc32 Error]: E2001 Ordinal type required
That's because the compiler cannot be sure that T
is an ordinal type. In order for it to do so, because T
is a generic type parameter, you would need there to impose a generic constraint that T
was an ordinal type. But the language supports no such constraint.
Upvotes: 2