The following program results in an error, raised in the execution of TOmniValue.CreateNamed
.
{$APPTYPE CONSOLE}
uses
OtlCommon;
var
Value: TOmniValue;
begin
Value := TOmniValue.CreateNamed([
'a', 42,
'b', 666
]);
Writeln(Value['a'].AsString);
Writeln(Value['b'].AsString);
end.
The exception is of type Exception
with message:
TOmniValue.CreateNamed: invalid name type
If the names are more than a single character in length then the code runs without error and reports the expected output.
Is there a problem with my code, or is there a problem with the library?
This appears to be a problem with the library. The implementation of CreateNamed
is:
constructor TOmniValue.CreateNamed(const values: array of const;
const cppDupConWorkaround: boolean);
var
i : integer;
name: string;
ovc : TOmniValueContainer;
begin
ovc := TOmniValueContainer.Create;
Assert(not Odd(Low(values)));
name := '';
for i := Low(values) to High(values) do begin
with values[i] do begin
if not Odd(i) then
case VType of
vtChar: name := string(VChar);
vtString: name := string(VString^);
vtPChar: name := string(StrPasA(VPChar));
vtAnsiString: name := string(VAnsiString);
vtVariant: name := string(VVariant^);
vtWideString: name := WideString(VWideString);
{$IFDEF UNICODE}
vtUnicodeString: name := string(VUnicodeString);
{$ENDIF UNICODE}
else
raise Exception.Create ('TOmniValue.CreateNamed: invalid name type')
end //case
else
case VType of
vtInteger: ovc.Add(VInteger, name);
vtBoolean: ovc.Add(VBoolean, name);
vtChar: ovc.Add(string(VChar), name);
vtExtended: ovc.Add(VExtended^, name);
vtString: ovc.Add(string(VString^), name);
vtPointer: ovc.Add(VPointer, name);
vtPChar: ovc.Add(string(StrPasA(VPChar)), name);
vtAnsiString: ovc.Add(AnsiString(VAnsiString), name);
vtCurrency: ovc.Add(VCurrency^, name);
vtVariant: ovc.Add(VVariant^, name);
vtObject: ovc.Add(VObject, name);
vtInterface: ovc.Add(IInterface(VInterface), name);
vtWideString: ovc.Add(WideString(VWideString), name);
vtInt64: ovc.Add(VInt64^, name);
{$IFDEF UNICODE}
vtUnicodeString: ovc.Add(string(VUnicodeString), name);
{$ENDIF UNICODE}
else
raise Exception.Create ('TOmniValue.CreateNamed: invalid data type')
end; //case
end; //with
end; //for i
SetAsArray(ovc);
end; { TOmniValue.CreateNamed }
The exception is raised by the first of the two raise
statements above. The exception is used to indicate that a value was supplied with a type that cannot be handled. As it turns out, the type of the value when you specify a string literal with length 1 is vtWideChar
. And indeed this type is not handled at all.
So you can work around the issue by forcing the call to CreateNamed
to receive strings rather than single characters:
Value := TOmniValue.CreateNamed([
string('a'), 42,
string('b'), 666
]);
In my view it would be better for the library to be modified like to accept single characters. It already handles AnsiChar
and I suspect it is a simple omission that it does not handle WideChar
. I think the code should read:
constructor TOmniValue.CreateNamed(const values: array of const;
const cppDupConWorkaround: boolean);
var
i : integer;
name: string;
ovc : TOmniValueContainer;
begin
ovc := TOmniValueContainer.Create;
Assert(not Odd(Low(values)));
name := '';
for i := Low(values) to High(values) do begin
with values[i] do begin
if not Odd(i) then
case VType of
vtChar: name := string(VChar);
vtString: name := string(VString^);
vtPChar: name := string(StrPasA(VPChar));
vtAnsiString: name := string(VAnsiString);
vtVariant: name := string(VVariant^);
vtWideString: name := WideString(VWideString);
vtWideChar: name := string(VWideChar);
{$IFDEF UNICODE}
vtUnicodeString: name := string(VUnicodeString);
{$ENDIF UNICODE}
else
raise Exception.Create ('TOmniValue.CreateNamed: invalid name type')
end //case
else
case VType of
vtInteger: ovc.Add(VInteger, name);
vtBoolean: ovc.Add(VBoolean, name);
vtChar: ovc.Add(string(VChar), name);
vtExtended: ovc.Add(VExtended^, name);
vtString: ovc.Add(string(VString^), name);
vtPointer: ovc.Add(VPointer, name);
vtPChar: ovc.Add(string(StrPasA(VPChar)), name);
vtAnsiString: ovc.Add(AnsiString(VAnsiString), name);
vtCurrency: ovc.Add(VCurrency^, name);
vtVariant: ovc.Add(VVariant^, name);
vtObject: ovc.Add(VObject, name);
vtInterface: ovc.Add(IInterface(VInterface), name);
vtWideString: ovc.Add(WideString(VWideString), name);
vtWideChar: ovc.Add(string(VWideChar), name);
vtInt64: ovc.Add(VInt64^, name);
{$IFDEF UNICODE}
vtUnicodeString: ovc.Add(string(VUnicodeString), name);
{$ENDIF UNICODE}
else
raise Exception.Create ('TOmniValue.CreateNamed: invalid data type')
end; //case
end; //with
end; //for i
SetAsArray(ovc);
end; { TOmniValue.CreateNamed }
Reported as: OTL issue #64