Jump to content

Recommended Posts

6 minutes ago, Kryvich said:

Don't take it to heart. Sometimes it is difficult to find the right English words, which makes my sentences sound too categorical.

And what is your native language, for me it is Russian, although I am a Bashkir by nationality.

Share this post


Link to post

I have several open-source projects, old and new, but there is no active use of enumerations. But they are actively used in my amateur projects related to linguistics and others.

To give a hint of how the enumerations can be used, take for example the syntactic analyzer messages, warnings and errors:

  TSyntaxMessage = (smNone,
    // Here are suggestions of syntax analyzer:
    smRareLinkForWord, // Rare syntactic link
    smFirstSuggestion = smRareLinkForWord,
    smAdjToVerb,
    //...
    smSuggestionN,
    smLastSuggestion = smSuggestionN,
    // Here are warnings
    smNotLinkedWord, 
    smFirstWarning = smNotLinkedWord,
    // ...
    smWarningN,
    smLastWarning = smWarningN
    // Here are errors
);

We have a list of messages logically divided into groups: 1) no message smNone, 2) suggestions smFirstSuggestion..smLastSuggestion, 3) warnings smFirstWarning..smLastWarning, 4)...

I don't need to set numerical values of members and check if they fall into the required sub-ranges. This happens automatically. Now I can write:

case SyntaxMessage of
  smNone: ; // All is OK
  smFirstSuggestion..smLastSuggestion: ProcessSuggestion;
  smFirstWarning..smLastWarning: ProcessWarning;
  else Assert(False, 'Ops...');
end;

We can create a helper to work with messages:

  TSyntaxMessageHelper = record helper for TSyntaxMessage
    class function FromString(S: string): TSyntaxMessageHelper; static; // Returns TSyntaxMessage by the name
    function ToString: string; // Returns the name of message
    function Text: string; // Get a text of user's message
  end;

And use this as follows:

Mess1 := TSyntaxMessage.FromString('smBadUseOfVerb'); // when loading from XML etc.
s := Mess1.ToString; // when saving to XML, JSON,...
ShowMessage(Mess1.Text);

Then we can declare a set of messages, add a helper for it, and work in a convenient manner.

  TSyntaxMessages = set of TSyntaxMessage;

  TSyntaxMessagesHelper = record helper for TSyntaxMessages
    class function FromString(S: string): TSyntaxMessages; static;
    function ToString: string;
    function Text: string;
    function First: TSyntaxMessage;
    function IsEmpty: Boolean;
    function Count: Integer;
    procedure ExcludeWarnings;
    procedure Include(const Messages: TSyntaxMessages);
    procedure Exclude(const Messages: TSyntaxMessages);
    procedure ForEach(Proc: TSomeProcedure);
    //...
  end;

MyMessages := TSyntaxMessages.FromString('smRareLinkForWord smAdjToVerb smNotLinkedWord');
if not MyMessages.IsEmpty then
  Mess := MyMessages.First;
MyMessages.Include(OtherMessages);
MyMessages.ForEach(DoSomething);

We can add inline to the method declarations for faster code.

A variable of type TSyntaxMessages will have a minimum size sufficient to store the set. The compiler will automatically check for ranges.

 

Edited by Kryvich
  • Thanks 2

Share this post


Link to post
21 hours ago, Stefan Glienke said:

If that would compile - and even if it would that would not prevent that set to be assigned [4] to.

Validation works for the range type, but this check fails for the set.

Enum set in Delphi is also not implemented perfect.

I've seen better and more correct implementations of the Pascal language.

 

procedure TestTsgList.Test1;
type
  TRange = 0..15;
  TSet = set of TRange;
  TMyEnum = (Neg = 1, Template = 4, Static = 15, Header = 2, Custom = 4);
  TMyEnumSet = set of TMyEnum;
var
  i, size: Integer;
  r: TRange;
  s: TSet;
  e: TMyEnum;
  es: TMyEnumSet;
begin
  size := sizeof(es);
  size := sizeof(s);
  // set of enum
  es := [TMyEnum(3), TMyEnum(10)];
  e := High(TMyEnum);
  e := Succ(e);
  Include(es, e);
  Include(es, TMyEnum(10));
  // set of range
  r := 15;
  r := Succ(r);
  Include(s, r);
end;

 

Share this post


Link to post
19 hours ago, Lars Fosdal said:

You store Enums as strings in the database? 

In one case in multi-project complex I use constants for datatype. I'm 100% sure they won't change except adding new values so I carve them in stone. Not an option I like currently, now I think I'd better use mnemonic chars for this. There are several cases where I use mnemonic chars (abstract sample - 'M' for male sex, 'F' for female, whatever else for any possible later addition).

For (de)serialization when dealing with files I use enum names and sometimes array of values if I don't want prefixed names to appear.

19 hours ago, Rollo62 said:

Yes, but also names might change over time, so thats no real benefit over numbers.

All right, numbers have a higher risk of being re-ordered, but in principle: "enums might change over time".

Everything might change but enum names usually change less frequently.

Share this post


Link to post

We mostly store the ordinal value of the enumerated type variable to an int in the database.
Yes, it is fragile with regards to people changing the enumeration - but we have guidelines and unit tests to avoid that.

type
  TPSDExpeditionType = (etRetailer,etWholesaler,etRetailerDairy,etWholesalerDairy);  // Do not reorder or remove - add at end

  TLocation = class
    ExpeditionType: TPSDExpeditionType;

We use RTTI to export the enums to a view in the database so that we can do logic in the SQL code using the same identifiers as we use in Delphi.
T-SQL doesn't do constants per se, but selecting from a flat static view is optimized by the query engine and is extremely low cost.
  
CREATE TABLE [dbo].[t_locations](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[ExpeditionType] [int] NOT NULL
--
CREATE VIEW [dbo].[v_psd_constants]
AS
SELECT        0 AS etRetailer, 1 AS etWholesaler, 2 AS etRetailerDairy, 3 AS etWholesalerDairy
--
SELECT * FROM v_Locations loc WHERE loc.ExpeditionType = (SELECT TOP(1) etRetailerDairy FROM v_psd_constants)


This means we can handle renames and additions - but not removals or reordering.  
From an SQL performance perspective, this runs circles around using strings.

  • Like 1
  • Thanks 1

Share this post


Link to post
8 minutes ago, Stefan Glienke said:

And if you would use tinyint you could even save 3 byte per record 😉

 

In most cases, yes 🙂
 

Share this post


Link to post
2 hours ago, Lars Fosdal said:

T-SQL doesn't do constants per se, but selecting from a flat static view is optimized by the query engine and is extremely low cost.

Pretty good option!

Share this post


Link to post
On 10/15/2020 at 2:16 PM, Rollo62 said:

I'm not 100% sure, since I never checked that, but I think class helpers won't work on consts.

I hope we are not talking about writing a helper to any specific value of the enumerated type?
Besides the declaration of constants, you can declare other related types for these constants, as well as methods to work with them.
Helpers for record can also be written.

  hEntity = record
    v: Cardinal;
  end;

  hEntityHelper = record Helper for hEntity
    constructor From (v: Cardinal);
    function FromRequest: Boolean; inline;
    function Request: hRequest; inline;
  end;

  TsdEntity = record
  const
    NoEntity: hEntity = (v: 0);

It would be possible to write methods directly in the type itself, but in our case there was a mutual dependence of handlers for several types. Helpers allowed to solve this problem in a graceful way.
Unlike the classes, I cannot write a forward declaration for writing. But at least I can declare pointers to a record which will be declared later, either in the interface part or in the implementation section.

Share this post


Link to post

I use enumerated types a lot, there is nothing wrong with them.

If you have an enumerated type parameter in a procedure, the compiler will not give you another substitution, unlike integer constants that can be confused.

 

However, no one bothers to use handlers instead of integer constants. This is usually an record with a number or
a packed record with a set of Subrange Types fields that can take up a comparable amount of memory.
By the way, I caught myself thinking, I have not experimented with packed records with enumerated fields.

 

In the handler, some of the bits can be set aside to determine the type, subtype, number of the object within this subtype.

Something like a decimal classifier, like the medical classifier of diseases.

I was the architect of the openEHR-based medical information system for the Tomsk Scientific Research Institute of Cardiology.

Such classifiers are ubiquitous in various databases and reference information systems.

Edited by Marat1961
wrong translation

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×