Restricting Generic to Primitive types

I’m finally spending the time to learn Oxygene and I’m running into what is probably a failure of my expectations to match reality.

So, I want a Complex number type that would be, in memory, pretty much what it would be if I wrote it in straight pascal or C. However, I want to leverage generics to vary the storage and precision (there is a library for Quads that I might want to leverage later on). I thought it would be simple to pass either Single or Double and then do some operator overloads for the appropriate math.

However, what I’m finding is that the type does not seem to be resolved enough for the add method to compile. I did write a bit of test code which declared a few TComplex<Double>.

I tried to work it up in Delphi with similar results. Now, everyone in the Delphi discord said ‘Of course you can’t do that because it doesn’t know what the type is’ in reference to the inability for the &add overload to compile. My counter is that since I did have something that fully specified what I wanted TComplex<Double> then the compiler does have enough information. If I don’t do anything with TComplex then it should be optimized out.

I tried using a where clause, yet that seems to AND single and double rather than OR. I guess what I’m trying to do is similar to Rust’s Sum (Enum) Type.

Am I just going down a rabbit hole?

BR

namespace fail.complex;

interface

uses
  RemObjects.Oxygene.System;

type
  TComplex<T> = record
    re: T;
    im: T;
  public
    constructor Create;
    class operator assign(var dest, src: TComplex<T>);
    class operator &add(const l, r: TComplex<T>): TComplex<T>;
  end;

implementation


{ TComplex<T> }

constructor TComplex<T>;
begin
  re := 0;  // Yeah, was wondering if it it would be fixed by bringing in a constructor....  
  im := 0;
end;

class operator TComplex<T>.&add(const l, r: TComplex<T>): TComplex<T>;
begin
  Result.re := l.re + r.re;  // Nu-uhhhh...
  Result.im := l.im + r.im; // ditto
end;

class operator TComplex<T>.assign(var dest, src: TComplex<T>);
begin
  dest.re := src.re;  
  dest.im := src.im;
end;
end.

Yeah, I’m afraid that wont work like this; the errors aer all “correct”. as rthe compiler doesnt know if T which could be anything can be assigned “0”, nt if T has a + operator.

What would happen if you declare var x: TComplex<Button>, for example?

In general, generic constraints are the rr to limit T to specific restrictions (such as implementing a specific interface). Alas, there exists noconstrait that wood help you there, as clstracints can only require to

  • have a actor
  • be a class or a record
  • *implement an interface

and none of those can provide the ability to assign 0 or to have a + operator.

That’s what I figured. I haven’t delved deep enough into Oxygene to determine if there is perhaps an Interface that is shared between real types exclusively. Even then its still not ideal because I cannot foresee a need for a complex number representing currency? (Maybe there is; beyond a passing familiarity with Black-Scholes, I am unfamiliar with Quantitative Finance).

My desire was to have it exist in memory as a simple structure with two fields of the same data-type. A generic record seemed perfect.

I’ve returned to finish this up. My thought was that the following would fix the issue

type
  TComplex<T> = record
    where T is System.Numerics.IFloatingPoint;
    re: T;
    im: T;
  public
    class operator assign(var dest, src: TComplex<T>);
    class operator &add(const l, r: TComplex<T>): TComplex<T>;
  end;

I know I must be looking for the snake that should have bitten me but for the life of me I’m not able to import System.Numerics.

What target platform is this? .NET 4.x for ,.NET Core? System.Numerics might be in an optional package you need to reference.

I’m using whatever is the default.

Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <PropertyGroup>
    <ProductVersion>3.5</ProductVersion>
    <RootNamespace>BlogPostProjects</RootNamespace>
    <ProjectGuid>{C1064F8D-1034-4312-91CE-8998C973958A}</ProjectGuid>
    <OutputType>Executable</OutputType>
    <BinaryName>BlogPostProjects</BinaryName>
    <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
    <DefaultUses>RemObjects.Elements.RTL</DefaultUses>
    <AllowLegacyCreate>True</AllowLegacyCreate>
    <DelphiCompatibility>True</DelphiCompatibility>
    <DelphiDivide>True</DelphiDivide>
    <AllowLegacyOutParams>True</AllowLegacyOutParams>
    <DefaultGlobalsToPublic>True</DefaultGlobalsToPublic>
    <AllowLegacyEnums>True</AllowLegacyEnums>
    <AllowLegacyWith>True</AllowLegacyWith>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
    <Optimize>False</Optimize>
    <OutputPath>.\Bin\Debug</OutputPath>
    <ConditionalDefines>DEBUG;TRACE;</ConditionalDefines>
    <GenerateDebugInfo>True</GenerateDebugInfo>
    <EnableUnmanagedDebugging>False</EnableUnmanagedDebugging>
    <EnableAsserts>True</EnableAsserts>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <OutputPath>.\Bin\Release</OutputPath>
    <EnableUnmanagedDebugging>False</EnableUnmanagedDebugging>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="gc" />
    <Reference Include="Island" />
    <Reference Include="rtl" />
    <Reference Include="Elements" />
    <Reference Include="winrt" />
    <Reference Include="Delphi" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.pas" />
    <Compile Include="..\..\infos\fail.complex.pas" />
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath)\RemObjects Software\Elements\RemObjects.Elements.Island.Windows.targets" />
</Projec

Oh, Island/Windows. System.Numerics is a .NET class library ;). You can only sue that in .NET projects.

Ah. And since my installed is borked, testing this out is going to be a trial, pardon the pun.

So, taking a look at the documentation on MS learn site, it makes its first appearance in .Net 7. I assume that’s still .Net Core . The .Net Framework does not have that particular interface. I’m unsure if it has an appropriate one.

Yes, .NET 5 and later are all based on .NET Core.