Jump to content

Lars Fosdal

Administrators
  • Content Count

    3524
  • Joined

  • Last visited

  • Days Won

    116

Posts posted by Lars Fosdal


  1. What is the format of the text message itself, i.e. not the attachment, but the body of text?

    If HTML - does it contain: <meta charset="UTF-8"> 

    If XML - does it contain: <?xml version = "1.0" encoding = "UTF-8" ?>

    If plain string - Do you explicitly transform the string from the internal unicode string to UTF-8?

     

    I also stumbled on this: https://stackoverflow.com/questions/40469867/message-rfc822-ignores-utf-8

    See last comment by @Remy Lebeau

     

    Quote

    RFC822 works fine with UTF-8 provided the email is using a compatible transfer encoding (8bit, quoted-printable, or base64)

     


  2. @Dany Marmur - I'm not a Dane, so it may be that the "seksti" / sixty is purely Norwegian.

    The standing Norwegian joke about the Danish language is that it is not a language, but a throat disease 😛

    That said, Norwegian was under Danish rule for hundreds of years, so the languages are quite close in structure as well as vocabulary. 

    Norwegian is "harder" / "crisper" in pronunciation, though - unless you consider the south-most dialects, neighboring Denmark 🙂


  3. Threads are dangerous in the same way that travel by car is dangerous.

    If you prepare well, know how to drive, follow the rules and take the appropriate precautions, then you will be in control and nothing bad happens.

    As always, there may be external factors that can cause problems - but that goes for code that is not executed in threads as well.

     

    That said - in this case, it sounds like the third party engine has serious flaws.

    • Like 1

  4. There doesn't seem to be a similar cross platform function.

     

    What is wrong with Thread.Terminate?

     

    You can override SetTerminate to set your own variables on termination.

    F.x. if you don't want to have a Terminated check in a tight loop, you could nil a variable you reference in the loop to raise an exception to get out of that loop.

    Ugly, yes - but not much uglier than TerminateThread.

     

     


  5.  
     
     
    1
    3 minutes ago, Fr0sT.Brutal said:

    O___O Wow, so weird! Didn't know that.

    You could also say that the half indicates a half twenty.  

     

    half tres = 2.5 x 20 = 50

    half firs = 3.5 x 20 = 70

    half fems = 4.5 x 20 = 90

     

    It is logical, but to a foreigner it does feel a bit jumbled - and why isn't there two twenty somethings like, 1.5 x 20 = 30, and 2 x 20 = 40?

    • Haha 1

  6. Just to illustrate the complexity of this in a different language, look at the danes that work with n times 20s and half-20s when above 50 and below 100, and they are not entirely consistent about it either.

    I am not a native dane, so there may be some mistakes - but this is my understanding of their classic spoken numbering.
     

    førr = forty = 40

    fem og førr = five and forty = 45

    half tres / femti = fifty = 3 x 20 - 10 = 50

    seks og halv tres = six and fifty =  6 + (3 x 20 - 10) = 56

    seksti / tres = sixty = 3 x 20 = 60

    fem og seksti = five and sixty = 65

    half firs / søtti = seventy = 4 x 20 - 10 = 70 

    åtti / firs = eighty = 4 x 20 = 80

    half fems / nitti = ninety = 5 x 20 - 10 = 90

    syv og halv fems = seven and ninety = 97

    hundrede / fems = hundred = 5 x 20 = 100 

     

    277 = to hundrede og syv og halv firs = two hundred and seven and seventy = 200 + 7 + 70

     

    Modern Danish does allow saying 55 = "femti fem" instead of "fem og halv tres" - i.e. more like the English spoken "fifty five", but the classic form is widely used.

    Modern 277 would be "to hundrede og søtti syv", like the English "two hundred and seventy seven".

    • Confused 1

  7. 14 hours ago, Mike Torrettinni said:

    Last question before I do some testing with new approaches:

     

    Would you reconsider your chosen approach if you have 100+ type of enums? If you have so much different data, that you need to repeat your implementation for 100+ times... would it make sense to still use your approach, or in such case you would (or have you) use something else?

     

    There is no definitive answer to that as YMMV. 

     

    We do literally have 100+ types of enums, and we implemented record helpers for each one, simply to ensure that all enums had the same capabilities. AsString, FromString, Name, and in some cases more texts, or biz.logic associated with the enum.

    We actually did the last push for this model this autumn, to get rid of the last couple of dozens of standalone TypeNameToStr functions.

     

    We also introduced app wide translations (Norwegian, Swedish, English) using Sisulizer.

    It turned out that using attributes for naming was a bit of a challenge, since you can't use a resourcestring as an argument to an attribute - go figure.

     

    resourcestring
      sName = 'Name';
    
    type
      AttrNameAttribute = class(TCustomAttribute)
        constructor Create(const aName: String);
      end;
    
    type
      TSomeType = class
      private
        FaName: string;
        procedure SetaName(const Value: string);
      public
        [AttrName(sName)] // <-- [dcc32 Error] : E2026 Constant expression expected
        property aName: string read FaName write SetaName;
      end;

     

    We ended up setting names explicitly in code instead.

    • Thanks 1

  8. 14 minutes ago, Mike Torrettinni said:

    Aha, I see. InitHelper initializes Caption, right? From const of names.., OR is Caption a function that return const_of_names[ptExternalDev]?

    InitHelper initializes the class var arrays for Caption and DevTitle (for the second example using class vars).  You only need to call this once for the application, so you could do f.x. it in the unit init section.

    For your second question, see the implementation of TProjectTypeHelper.Caption.

     

    Why the extra class function CaptionOf? A habit of mine, due to the rule of only one class helper for a type in scope at a time. 


  9. Both.  See updated post.

     

    Let me correct myself...

    With a record helper, the methods of the helper become part of the helped type.

     

    begin 
      TProjectType.InitClass; // only for the second example
      var prj: TProjectType = ptMain;
      writeln(prj.Caption);
      writeln(ptMain.Caption)
      writeln(TProjectType.CaptionOf(ptMain))
    end;

    all output the same string.


    Populating a combobox can be done like this

      for var prj := Low(TProjectType) to High(TProjectType)
       do ComboBox1.AddItem(prj.Caption, Pointer(Ord(prj))); 

    and later, if you like such ugly hacks 😉

      if ComboBox1.ItemIndex >= 0
      then begin
        var selectedprj := TProjectType(Integer(ComboBox1.Items[ComboBox1.ItemIndex]));
      end;

     

    • Thanks 1

  10. I prefer record helpers, combined with the const arrays.  I really wish I could make generic record helpers for arrays, though 😕

     

    type
      TProjectType = (ptMain, ptSub, ptExternalDev, ptInternalDev);
      TProjectTypeHelper = record helper for TProjectType
      private
        const
          cProjDefValues: array[TProjectType] of string = ('main_proj', 'sub_proj', 'dev_ext', 'dev_int');
          cProjCaptions: array[TProjectType] of string = ('Main Project', 'Sub Project', 'External Dev Project', 'Internal Dev Project');
      public
        class function DefValueOf(const aProjectType: TProjectType): string; static;
        class function CaptionOf(const aProjectType: TProjectType): string; static;
        function DefValue: string;
        function Caption: string;
      end;
    
    implementation
    
    { TProjectTypeHelper }
    
    function TProjectTypeHelper.Caption: string;
    begin
      Result := CaptionOf(Self);
    end;
    
    class function TProjectTypeHelper.CaptionOf(const aProjectType: TProjectType): string;
    begin
      Result := cProjCaptions[aProjectType];
    end;
    
    function TProjectTypeHelper.DefValue: string;
    begin
      Result := DefValueOf(Self);
    end;
    
    class function TProjectTypeHelper.DefValueOf(const aProjectType: TProjectType): string;
    begin
      Result := cProjDefValues[aProjectType];
    end;


    The class functions could also be case statements - but the array[type] ensures you have values for all.  The weak spot here is if you insert a value in the constant list, or reorder values - without doing the same for the strings.

     

    Or you can use class vars - which would make it easier to load up different languages at runtime, if so needed.

     

    type
      TProjectType = (ptMain, ptSub, ptExternalDev, ptInternalDev);
      TProjectTypeHelper = record helper for TProjectType
      private
      class var
        cProjDefValues: array[TProjectType] of string;
        cProjCaptions: array[TProjectType] of string;
      public
        class procedure InitHelper; static;
        class function DefValueOf(const aProjectType: TProjectType): string; static;
        class function CaptionOf(const aProjectType: TProjectType): string; static;
        function DefValue: string;
        function Caption: string;
      end;
    
    implementation
    
    { TProjectTypeHelper }
    
    function TProjectTypeHelper.Caption: string;
    begin
      Result := CaptionOf(Self);
    end;
    
    class function TProjectTypeHelper.CaptionOf(const aProjectType: TProjectType): string;
    begin
      Result := cProjCaptions[aProjectType];
    end;
    
    function TProjectTypeHelper.DefValue: string;
    begin
      Result := DefValueOf(Self);
    end;
    
    class function TProjectTypeHelper.DefValueOf(const aProjectType: TProjectType): string;
    begin
      Result := cProjDefValues[aProjectType];
    end;
    
    class procedure TProjectTypeHelper.InitHelper;
    begin
      cProjDefValues[ptMain] :=         'main_proj';
      cProjCaptions [ptMain] :=         'Main Project';
    
      cProjDefValues[ptSub] :=          'sub_proj';
      cProjCaptions [ptSub] :=          'Sub Project';
    
      cProjDefValues[ptExternalDev] :=  'dev_ext';
      cProjCaptions [ptExternalDev] :=  'External Dev Project';
    
      cProjDefValues[ptInternalDev] :=  'dev_int';
      cProjCaptions [ptInternalDev] :=  'Internal Dev Project';
    end;
    

     

     

    • Thanks 1

  11. @Markus Kinzler - What I meant is that in UDP, there is no mechanism the ensures that the packet reaches its destination, nor is there a mechanism that allows the recipient to discover that a packet has gone missing.

    As you say, all of that would have to be implemented as a protocol on top of UDP.

    When it comes to the content, I actually don't know if UDP ensures that the content of a delivered package is guaranteed to be intact? I would tend to assume so, but I haven't actually checked.


  12. On 11/5/2019 at 2:33 PM, Fr0sT.Brutal said:

    M$ tools maybe... Oracle probably (as they both own the language itself) but I doubt about other ones. Anyway the debugger could be attached to any running process so these DLL's could be debugged with RAD studio / MS VS / whatever.

     

     

    We use a couple of C# assemblies - mostly for being able to break down stuff like bar code scan strings into AIs, or other forms of string analysis, validation (checksums) or manipulation.
    It is a bit cumbersome, tbh, but it is a lot more efficient than trying to do the same stuff in T-SQL.

    • Like 1
×