'Pass a mixture of differend enums types in delphi

I need to write a procedure where can pass different enum selections.

type
  TEnumOne = (eOneFlagOne, eOneFlagTwo);
  TEnumTwo = (eTwoFlagOne, eTwoFlagTwo);

The method should take different enums:

Process([eOneFlagOne, eTwoFlagTwo]);

I'm trying to implement it like this:

// does not work!
procedure Process(const Enums: array of Variant);
var aValue; Variant
begin
  for aValue in Enums do
  begin
    // of course, can't work...
    if aValue is TEnumOne then
  end; 
end;

So, is there a type instead of Variant, I can choose? Or is the a different approach, I dont' see?



Solution 1:[1]

Meet the beauty of pascal.

Here's a working example of what you're probably trying to do:

program Project34; {$APPTYPE CONSOLE}

type
  TEnum=(eOneFlagOne,eOneFlagTwo,eTwoFlagOne,eTwoFlagTwo);
  TEnumSet=set of TEnum;

const
  cEnumOne=[eOneFlagOne,eOneFlagTwo];
  cEnumTwo=[eTwoFlagOne,eTwoFlagTwo];

procedure Process(const Enums: TEnumSEt);
var e:TEnum;
begin
  for e in Enums do
    WriteLn(ord(e));
end;

begin
  Process([eOneFlagOne, eTwoFlagTwo]);
  Process(cEnumOne);
  Process(cEnumTwo);
end.

Note that you can also declare the constants like this. Maybe that's clearer:

const
  cEnumOne:TEnumSet=[eOneFlagOne,eOneFlagTwo];
  cEnumTwo:TEnumSet=[eTwoFlagOne,eTwoFlagTwo];

Solution 2:[2]

To be blunt, when you start trying to bend your language like this, it usually means that your approach is probably wrong. (not always, but usually) I'd be interested to hear what problem you are trying to solve, as maybe there is a better design option.

With what little we know about your problem, I would suggest that either you create 2 functions with the different signatures.
Or
if the required logic branch is similar enough to each other, then you could create a Generic method (assuming Delphi 2009 or higher) using the enum type as your generic parm.

...
procedure Process<T>(const enumParam : T) // Add a generic constraint here as well
begin
...
end;

Seems likely to me however, that the 2 different methods would be the likely best option (or something else entirely)

Solution 3:[3]

RRUZ deleted his answer, here is a reworked version with type safety. RTTI is used to identify the different enum constants.

function EnumToString(const TypeInfo : pTypeInfo; Ix : Integer) : string;
begin
  Result := GetEnumName(TypeInfo, ix); 
end;

procedure Process( const Args : array of string);
var
  LIndex,ix : integer;
  EnumOne : TEnumOne;
  EnumTwo : TEnumTwo;
begin
  for LIndex := 0 to High(Args) do begin
    ix := GetEnumValue( TypeInfo(TEnumOne), Args[LIndex]);
    if (ix <> -1) then
    begin
      EnumOne := TEnumOne( ix); 
      // do something with EnumOne
      ...
      continue;
    end;

    ix := GetEnumValue( TypeInfo(TEnumTwo), Args[LIndex]);
    if (ix <> -1) then
    begin
      EnumTwo := TEnumTwo( ix); 
      // do something with EnumTwo
      ...
      continue;
    end; 
    ... 
    etc

  end;        
end;

Process( [EnumToString(TypeInfo(TEnumOne),Ord(TEnumOne.eOneFlagOne)),
          EnumToString(TypeInfo(TEnumTwo),Ord(TEnumTwo.eTwoFlagTwo))]);

Solution 4:[4]

An alternative is to keep it as just one enum:

TEnum = (eOneFlagOne, eOneFlagTwo, eTwoFlagOne, eTwoFlagTwo);

The Process function would then look like this:

procedure Process(Enums: Array of TEnum);
var 
  aValue: TEnum;
begin
  for aValue in Enums do
  begin
    if aValue in [eOneFlagOne, eOneFlagTwo] then
      // Handle the eOne enums
    else if aValue in [eTwoFlagOne, eTwoFlagTwo] then
      // Handle the eTwo enums
  end; 
end;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Tim Jarvis
Solution 3
Solution 4 bjaastad_e