Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Non-zero based enum in published property displayed incorrectly


This question is answered.


Permlink Replies: 6 - Last Post: Feb 1, 2018 12:26 PM Last Post By: Ted Lyngmo
Ted Lyngmo

Posts: 117
Registered: 10/3/06
Non-zero based enum in published property displayed incorrectly  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 2:53 PM
I'm trying to clean up some code in an old COM port component but when doing so, I'm not able to get the drop down list for some of my properties filled in with the enumerated values available. I've stripped this down quite a bit - hopefully not too much.
// .hpp -----------------------------------------------------------------------------------------------------
enum bsType { bs4=4, bs5, bs6, bs7, bs8 }; // starting at 4
enum paType { paNo=NOPARITY, paOdd=ODDPARITY, paEven=EVENPARITY, paMark=MARKPARITY, paSpace=SPACEPARITY };
 
class PACKAGE MComPort : public TComponent
{
private:
	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx
	DCB FSetup;
	AnsiString FPortName;
 
	HANDLE Handle;
	MComPortThread *PThread;
 
protected:
	virtual bsType __fastcall GetByteSize();
	virtual void __fastcall SetByteSize( bsType x );
 
public:
	__fastcall MComPort(TComponent* Owner);
 
__published:
	__property bsType ByteSize = { read=GetByteSize, write=SetByteSize, default=bs8 };
};
 
// .cpp ---------------------------------------------------------------------------------------------------
__fastcall MComPort::MComPort(TComponent* Owner) :
	TComponent(Owner),
	FSetup(),
	FPortName("\\\\.\\COM1"),
	Handle(INVALID_HANDLE_VALUE),
	PThread(NULL)
{
	// Fixed port values (not published)
	FSetup.DCBlength = sizeof( DCB );
	FSetup.fBinary = true;
	FSetup.wReserved = 0;
 
	// Published default port values:
	BaudRate    = br9600;
	ByteSize    = bs8;
	Parity      = paNo;
}
 
bsType __fastcall MComPort::GetByteSize() { return static_cast<bsType>(FSetup.ByteSize); }
void __fastcall MComPort::SetByteSize( bsType x ) { FSetup.ByteSize = static_cast<BYTE>(x); }

In the Properties editor, the ByteSize has a drop down, but the content is garbled. One entry is filled with control characters and the name "paType" (an enum type used for the parity). Another is "bs8" (from the enum I want) and the third is "ComPort". I was expecting "bs4" up to "bs8".

If I make the enum zero based and tweak the setter and getter, it works ok. This is roughly what the code looked when I wrote it in BCB4:
enum bsType { bs4, bs5, bs6, bs7, bs8 };  // starting at 0
 
bsType __fastcall MComPort::GetByteSize() { return static_cast<bsType>(FSetup.ByteSize+4); } // +4 hack
void __fastcall MComPort::SetByteSize( bsType x ) { FSetup.ByteSize = static_cast<BYTE>(x-4); }  // -4 hack

I also have an enum with predefined values for BaudRate and that list is very long and filled with gaps so I'd rather not have to do a translation table for it.

I get the same problem in both RAD2007 and 10.2. Any ideas what I'm missing?

Br,
Ted
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Non-zero based enum in published property displayed incorrectly
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 4:05 PM   in response to: Ted Lyngmo in response to: Ted Lyngmo
Ted Lyngmo wrote:

enum bsType { bs4=4, bs5, bs6, bs7, bs8 }; // starting at 4

That enum WILL NOT work for a **published** property.

An enum used for a **published** property **MUST** start at 0 and have
sequential values. RTTI used by the DFM streaming system is simply not
rich enough to handle enums that do not follow this convention. This
is documented behavior.

You will have to change the bsType enum accordingly, and then map it to
the correct values when passing it to the COM port API (you don't need
to do this for your paType enum, as it already follows the required
convention, since the API parity values are defined as 0-4, but I
wouldn't suggest relying on that behavior), eg:

enum bsType { bs4, bs5, bs6, bs7, bs8 };
 
enum paType { paNo, paOdd, paEven, paMark, paSpace };
 
...
 
bsType __fastcall GetByteSize() { return
static_cast<bsType>(FSetup.ByteSize-4); }
 
void __fastcall SetByteSize(bsType Value) { FSetup.ByteSize =
static_cast<BYTE>(Value) + 4; }
 
...
 
paType __fastcall GetParity() { return
static_cast<paType>(FSetup.Parity); }
 
void __fastcall SetParity(paType Value) { FSetup.Parity =
static_cast<BYTE>(Value); }
 
...
 
__property bsType ByteSize = { read=GetByteSize, write=SetByteSize,
default=bs8 };
 
__property paType Parity = { read=GetParity, write=SetParity,
default=paNo };


An enum used for a **non-published** property, on the other hand, can
use whatever values you want, in whatever order you want, since such
properties are not tied to RTTI.

In the Properties editor, the ByteSize has a drop down, but the
content is garbled.

Yes, because RTTI doesn't work for enums that do not start with 0,
and/or are not sequential.

If I make the enum zero based and tweak the setter and getter, it
works ok.

Yes, that is what you **MUST** do in this situation.

Besides, it is good design anyway to not leak platform-specific
details. The actual API values should be hidden inside your
component's implementation code, where they belong when your component
translates from its own values to API values and back.

I also have an enum with predefined values for BaudRate and that list
is very long and filled with gaps so I'd rather not have to do a
translation table for it.

I would not suggest using an enum for that property. The API's
BaudRate is much more flexible than its ByteSize and Parity. The
actual BaudRate can be any user-defined value that the COM device
supports, it is not limited to just the 14 values that the API
pre-defines. Unlike ByteSize and Parity, which are limited to just a
few values, so an enum makes more sense for them.

I would use a plain 'int' for the BaudRate property, and then for added
convenience, I would create a custom property editor to display a
drop-down list for the pre-defined values, but still allow the user to
type in any value they want.

--
Remy Lebeau (TeamB)
Ted Lyngmo

Posts: 117
Registered: 10/3/06
Re: Non-zero based enum in published property displayed incorrectly  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 12:51 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
If I make the enum zero based and tweak the setter and getter, it
works ok.

Yes, that is what you **MUST** do in this situation.

Besides, it is good design anyway to not leak platform-specific
details. The actual API values should be hidden inside your
component's implementation code, where they belong when your
component translates from its own values to API values and back.

Oh, ok, I'd prefer to make it possible for a user of the component to programmatically calculate and set the real value in addition to be able to use the enums in the properties editor. Since that can't be done, perhaps I'll skip the enum and do this instead:
uint8_t __fastcall MComPort::GetByteSize() { return static_cast<uint8_t>(FSetup.ByteSize); }
void __fastcall MComPort::SetByteSize( uint8_t x ) {
    if( x<4 ) x=4;
    else if( x>8 ) x=8;
    FSetup.ByteSize = static_cast<BYTE>(x);
}

...or something similar.

I also have an enum with predefined values for BaudRate and that list
is very long and filled with gaps so I'd rather not have to do a
translation table for it.

I would not suggest using an enum for that property. The API's
BaudRate is much more flexible than its ByteSize and Parity. The
actual BaudRate can be any user-defined value that the COM device
supports, it is not limited to just the 14 values that the API
pre-defines. Unlike ByteSize and Parity, which are limited to just a
few values, so an enum makes more sense for them.

True.

I would use a plain 'int' for the BaudRate property, and then for added
convenience, I would create a custom property editor to display a
drop-down list for the pre-defined values, but still allow the user to
type in any value they want.

Sounds nice. I haven't created a custom property editor before but I'll look into that.

Thanks,
Ted
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Non-zero based enum in published property displayed incorrectly
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 9:03 AM   in response to: Ted Lyngmo in response to: Ted Lyngmo
Ted Lyngmo wrote:

I'd prefer to make it possible for a user of the component to
programmatically calculate and set the real value in addition to be
able to use the enums in the properties editor. Since that can't be
done

Yes, it can, and I already explained how - make the property use an
integer type so the user can assign whatever value they want, and then
create a custom property editor to allow the user to select from
pre-determined values at design-time.

perhaps I'll skip the enum and do this instead:

uint8_t __fastcall MComPort::GetByteSize() { return
static_cast<uint8_t>(FSetup.ByteSize); }
 
void __fastcall MComPort::SetByteSize( uint8_t x ) {
    if( x<4 ) x=4;
    else if( x>8 ) x=8;
    FSetup.ByteSize = static_cast<BYTE>(x);
}


...or something similar.

I would stick with the enum approach for the ByteSize and Parity
properties, since their allowed values are limited. If the user wants
to assign a numeric value dynamically, let them do their own casting in
their own code to ap their calculated values to your enum values.

Sounds nice. I haven't created a custom property editor before but
I'll look into that.

In this case, I would:

- create the property with an 'int' data type

- derive a class from TIntegerProperty, overriding the virtual
GetAttributes() method to include the paValueList flag in addition to
the default flags, and overriding the virtual GetValues() method to
provide the desired pre-defined values.

- register the editor class for the property using the
RegisterPropertyEditor() function in a design-time package (can't be
called in a runtime package, like RegisterComponents() can).

--
Remy Lebeau (TeamB)
Ted Lyngmo

Posts: 117
Registered: 10/3/06
Re: Non-zero based enum in published property displayed incorrectly  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 10:54 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Ted Lyngmo wrote:

I'd prefer to make it possible for a user of the component to
programmatically calculate and set the real value in addition to be
able to use the enums in the properties editor. Since that can't be
done

Yes, it can, and I already explained how - make the property use an
integer type so the user can assign whatever value they want, and then
create a custom property editor to allow the user to select from
pre-determined values at design-time.

True, I meant using the lazy non-zero based enum approach.

perhaps I'll skip the enum and do this instead:

uint8_t __fastcall MComPort::GetByteSize() { return
static_cast<uint8_t>(FSetup.ByteSize); }
 
void __fastcall MComPort::SetByteSize( uint8_t x ) {
    if( x<4 ) x=4;
    else if( x>8 ) x=8;
    FSetup.ByteSize = static_cast<BYTE>(x);
}


...or something similar.

I would stick with the enum approach for the ByteSize

Haven't really decided. It's one thing when there's a set of choices where their numerical values doesn't have real meaning and "++value" or "is choice A < choice B" doesn't make sense - so even though the ByteSize range is limited I'm tempted to do something like the above.

Though, one thing I noticed when trying out using "uint8_t" was that it is treated like an "unsigned char" in the Property editor, which doesn't look so good, so I made it uint16_t instead.

and Parity properties, since their allowed values are limited.

Yes, parity and the others will keep their enums.

If the user wants to assign a numeric value dynamically, let them
do their own casting in their own code to ap their calculated values
to your enum values.

Contemplating it... :)

Sounds nice. I haven't created a custom property editor before but
I'll look into that.

In this case, I would:

- create the property with an 'int' data type

- derive a class from TIntegerProperty, overriding the virtual
GetAttributes() method to include the paValueList flag in addition to
the default flags, and overriding the virtual GetValues() method to
provide the desired pre-defined values.

- register the editor class for the property using the
RegisterPropertyEditor() function in a design-time package (can't be
called in a runtime package, like RegisterComponents() can).

Ok, I'll see how far I get. I haven't created a component in many many years so it's almost all new to me. Many thanks for the hints.

Br,
Ted
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Non-zero based enum in published property displayed incorrectly  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 4:46 PM   in response to: Ted Lyngmo in response to: Ted Lyngmo
Ted Lyngmo wrote:

Though, one thing I noticed when trying out using "uint8_t" was that
it is treated like an "unsigned char" in the Property editor

Because it really is an 'unsigned char'. That is the actual data type
that uint8_t is an alias of.

--
Remy Lebeau (TeamB)
Ted Lyngmo

Posts: 117
Registered: 10/3/06
Re: Non-zero based enum in published property displayed incorrectly  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 1, 2018 12:26 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Ted Lyngmo wrote:

Though, one thing I noticed when trying out using "uint8_t" was that
it is treated like an "unsigned char" in the Property editor

Because it really is an 'unsigned char'. That is the actual data type
that uint8_t is an alias of.

Yes, I'm aware of that but was kind of hoping for some ... magic. Desperation I guess. Oh well, I think I'll stick with uint16_t in the Property editor and check the boundaries in the setter.

Br,
Ted
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02