[fpc-devel] SetPropValue raises exception with Enum types (Bug #4738)

Graeme Geldenhuys graemeg.lists at gmail.com
Sun Jan 29 00:49:15 CET 2006


Description:
I create a copy of an Object that has an enumerated type in the
Published section. The sample code only copies Published properties.
When it tried to assign the enumerated type to the new object, it
raises an exception.
I double checked that the GetPropValue does read the property
correctly to a local variant, before it tries to assign it to the new
object.

---------- Sample output ----------------------------
C:\FPC\BugTests\SetPropValue>project1.exe
Creating first person
Person One is a 20 year old Unknown.

Creating second person
Person Two is a 30 year old Male.

Creating clone of Person One
   lPropValue is Ordinal
An unhandled exception occurred at $00401514 :
Exception : Error setting property TPerson.Gender Message



-------------- Program ------------------------
program project1;
{$mode objfpc}{$H+}
uses
  Classes, SysUtils, Variants, TypInfo;

const
  ctkString = [ tkChar, tkString, tkWChar, tkLString, tkWString, tkAString ];
  ctkInt    = [ tkInteger, tkInt64 ];
  ctkFloat  = [ tkFloat ];
  ctkSimple = ctkString + ctkInt + ctkFloat;
  cErrorSettingProperty      = 'Error setting property %s.%s Message %s';

type
  TGender = (genUnknown, genMale, genFemale);
  TtiObject = class;

const
  cGenderGUI : array[TGender] of string =
    ( 'Unknown', 'Male', 'Female' );

type
  TtiObject = class(TPersistent)
  protected
    function    GetCaption: string; virtual;
    procedure   AssignPublishedProp(pSource: TtiObject; psPropName: string);
    procedure   AssignPublishedProps(pSource: TtiObject; pPropFilter:
                    TTypeKinds = []);
  public
    procedure   Assign(const pSource: TtiObject); reintroduce; virtual;
  end;

  TPerson = class(TtiObject)
  private
    FAge: integer;
    FGender: TGender;
    FName: String;
  public
    constructor Create;
    function    ToString: string;
  published
    property    Name: String read FName write FName;
    property    Age: integer read FAge write FAge;
    property    Gender: TGender read FGender write FGender;
  end;

procedure tiGetPropertyNames( pPersistent: TPersistentClass;
                              pSL: TStringList;
                              pPropFilter: TTypeKinds = ctkSimple );
var
  lCount : integer ;
  lSize  : integer ;
  lList  : PPropList ;
  i : integer ;
  lPropFilter : TTypeKinds ;
begin
  Assert( pSL <> nil, 'pSL not assigned.' ) ;
  lPropFilter := pPropFilter ;

  pSL.Clear ;

  lCount  := GetPropList(pPersistent.ClassInfo, lPropFilter, nil, false);
  lSize   := lCount * SizeOf(Pointer);
  GetMem(lList, lSize);
  try
    GetPropList(pPersistent.ClassInfo, lPropFilter, lList, false);
    for i := 0 to lcount - 1 do
      pSL.Add( lList^[i]^.Name );
  finally
    FreeMem( lList, lSize );
  end ;
end ;

procedure tiGetPropertyNames( pPersistent: TPersistent; pSL: TStringList;
                              pPropFilter: TTypeKinds = ctkSimple );
begin
  Assert( pPersistent <> nil, 'pPersistent not assigned.' ) ;
  tiGetPropertyNames( TPersistentClass( pPersistent.ClassType ),
                      pSL,
                      pPropFilter );
end;

{ TPerson }

function TPerson.ToString: string;
const
  C = '%s is a %d year old %s.';
begin
  Result := Format(C, [Name, Age, cGenderGUI[Gender]]);
end;

constructor TPerson.Create;
begin
  inherited Create;
  FGender := genUnknown;
end;

{ TtiObject }

procedure TtiObject.AssignPublishedProps( pSource: TtiObject;
                                          pPropFilter: TTypeKinds);
var
  lsl: TStringList;
  i: integer;
  lsPropName: string;
  lPropFilter: TTypeKinds;
begin
  if pPropFilter = [] then
    lPropFilter := ctkSimple + [tkEnumeration, tkVariant]
  else
    lPropFilter := pPropFilter;

  lsl := TStringList.Create;
  try
    tiGetPropertyNames(self, lsl, lPropFilter);
    for i := 0 to lsl.Count - 1 do
    begin
      lsPropName := lsl.Strings[i];
      try
        AssignPublishedProp( pSource, lsPropName ) ;
      except
        on e: Exception do
          raise Exception.CreateFmt(cErrorSettingProperty,
            [ClassName, lsPropName, e.Message]);
      end ;
    end ;
  finally
    lsl.Free ;
  end ;
end ;

procedure TtiObject.Assign(const pSource: TtiObject);
begin
  Assert(( pSource is Self.ClassType ) or
          ( Self is pSource.ClassType ),
          pSource.ClassName +
          ' and ' +
          ClassName +
          ' are not assignment compatable' ) ;

  AssignPublishedProps( pSource ) ;
end;

function TtiObject.GetCaption: string;
begin
  Result := ClassName;
end;

procedure TtiObject.AssignPublishedProp( pSource: TtiObject;
                                         psPropName: string);
var
  lPropType: TTypeKind;
  lPropValue: Variant;
begin
  lPropType := TypInfo.PropType( pSource, psPropName ) ;
  if lPropType in ctkSimple + [tkVariant, tkEnumeration] then
  begin
    lPropValue := TypInfo.GetPropValue(pSource, psPropName);
    if VarIsOrdinal(lPropValue) then
      writeln('   lPropValue is Ordinal');

    TypInfo.SetPropValue( Self, psPropName, lPropValue);
  end
  else
    raise Exception.CreateFmt(cErrorSettingProperty,
      [ClassName, psPropName, 'Unknown property type']);
end;


var
  lData1: TPerson;
  lData2: TPerson;
begin
  Writeln('Creating first person');
  lData1 := TPerson.Create;
  lData1.Name   := 'Person One';
  lData1.Age    := 20;
  Writeln(lData1.ToString);
  Writeln('');

  Writeln('Creating second person');
  lData2 := TPerson.Create;
  lData2.Name   := 'Person Two';
  lData2.Age    := 30;
  lData2.Gender := genMale;
  Writeln(lData2.ToString);
  Writeln('');

  Writeln('Creating clone of Person One');
  lData2.Assign(lData1);

  Writeln('Person Two is now:');
  Writeln(lData2.ToString);

  lData1.Free;
  lData2.Free;
end.
----------------------------------------------

Regards,
  - Graeme -



More information about the fpc-devel mailing list