The following declaration:
type
TRec = record
constructor Create;
end;
produces this compilation error:
E2394 Parameterless constructors not allowed on record types
The documentation rather unhelpfully states:
No further information is available for this error or warning.
My question is why the language was designed this way. Was it done this way purely to echo the analogous restriction for C# structs?
The language guide says this:
Records are constructed automatically, using a default no-argument constructor, but classes must be explicitly constructed. Because records have a default no-argument constructor, any user-defined record constructor must have one or more parameters.
But that doesn't make much sense. If there is a default constructor for a record, it can't be found through RTTI. And even if there was, why would that imply that it was impossible to add another one? You can do so for classes.
Perhaps the rationale is that if we were allowed to define our own parameterless constructors, we'd expect the compiler to call them automatically.
Note: I understand that you can use a parameterless static class function as a workaround. Indeed, I personally always prefer to use static class function instead of record constructors. But that's not the point of the question. What I really want to know is why parameterless constructors are not allowed on record types.
I can't give you a definitive answer (only the compiler builders can), but I suspect it is not related to Delphi's .NET past, but rather to Delphi's relation with C++Builder.
As cppreference says:
A default constructor is a constructor which can be called with no arguments (either defined with an empty parameter list, or with default arguments provided for every parameter).
C++ allows for parameterless constructors, and these parameterless constructors would become the default constructor, in C++. A default constructor is called in many situations, e.g. if you simply declare:
Foo myFoo;
The default constructor is called. This does not happen in Delphi, but a C++ programmer might expect it. Similarly, if you do:
Foo elements[1000];
The default constructor is called on each element (I checked that). This also doesn't happen in Delphi, although a C++ programmer might expect it.
Other hints that this is C++-related:
Constructors with different names (e.g. Init) are not allowed either. This seems to point to conflicts with C++ or with C#, as in both, constructors have the name of the class or struct, so any parameterless constructor would be mapped to Foo() (in a struct or class called Foo.)
Constructors with only default parameters are not allowed either. This matches the cppreference description for default constructors with only default arguments.
All in all, there are hints that parameterless constructors (or ones with only default parameters) conflict with C++ (i.e. C++Builder) and that that is why they are not allowed.
Note that this is not the only restriction caused by differences with C++: e.g. in Delphi you can't cast integers to and from floating point types either, because in C and C++, that would cause a conversion, while in Delphi, it would merely cause a reinterpretation of the bits. In order not to confuse people who were coming to Delphi from C or C++, the casting restriction was placed on floating point types. There may be more.
Related
How can i make a typecast using the object classType parameter?
(obj as obj.classType).items[i]...//obj.classType = TList<myType>
This code is rejected by the compiler. But, moreover, I need to get access to the properties of the object, no matter what class (TList) it possesses:
system.classes.TList
system.generics.collections.TList<T>
In my program there is an object that actually belongs to the class TList<T>, and I am afraid that casting to a system.classes.TList type may cause some errors in my program.
It's impossible to typecast like this. That is because Delphi is a statically typed language.
Imagibe you could write code like this:
(obj as obj.classType).items
Since the value if obj.classType is unknown at compile time, the compiler cannot, at compile time, know whether items even exists, never mind how to access it and so on.
As for your goal regarding list classes, the generic and non-generic list types do not share a common ancestor beyond TObject. What you are attempting is simply impossible.
Whatever your problem is, you'll need to find a different solution.
See for example Data.Maybe.Base in the stdlib — all Maybe, Any, and All have a just constructor.
Agda allows these definitions. How can one specify which one to use?
Each data type comes with its own module. So Maybe, All and Any are all type constructors and modules simultaneously. Thus you can write Maybe.just, All.just or Any.just to disambiguate the constructor. Or it can be disambiguated by type inference (unification is a more appropriate term) or an explicit type signature like Thilo said in their comment. (It's not true however that you'll get an error if there some ambiguity -- you'll get an unsolved meta).
The attribute #inline(__always) forces the compiler to inline a particular function. How is code provided by external libraries inlined in one's project? Does the compiler actually copy code segments from the library's executable?
As far as I'm aware, #inline(__always) means that the function (or similar) is always inlined, no matter what. That means that its symbols are exposed in the compiled module so they can be inlined by projects consuming that module. Hence the "always".
This is largely an undocumented attribute, with the only official references I can find being in some stdlib devs' internal documentation, not even describing its behavior directly. The best unofficial documentation I can find is Vandad Nahavandipoor's disassembly-confirmed investigation into its behavior, which doesn't attempt to confirm the cross-module use case you are concerned with.
In Swift 4.2, #inlinable and #usableFromInline were introduced to finish this story.
My understanding:
#inline(__always) forces functions (et al) to be inlined every time no matter where they're declared or used
#inlinable allows functions (et al) in a module to be inlined into calling code in that module, or calling code using that module, if the compiler deems it necessary
#usableFromInline allows functions (et al) internal to a module to be inlined into #inlinable calling code that's also in that module, if the compiler deems it necessary. Unlike #inlinable, these must be internal; they cannot be public
According to Swift.org:
inlinable
Apply this attribute to a function, method, computed property, subscript, convenience initializer, or deinitializer declaration to expose that declaration’s implementation as part of the module’s public interface. The compiler is allowed to replace calls to an inlinable symbol with a copy of the symbol’s implementation at the call site.
Inlinable code can interact with public symbols declared in any module, and it can interact with internal symbols declared in the same module that are marked with the usableFromInline attribute. Inlinable code can’t interact with private or fileprivate symbols.
This attribute can’t be applied to declarations that are nested inside functions or to fileprivate or private declarations. Functions and closures that are defined inside an inlinable function are implicitly inlinable, even though they can’t be marked with this attribute.
usableFromInline
Apply this attribute to a function, method, computed property, subscript, initializer, or deinitializer declaration to allow that symbol to be used in inlinable code that’s defined in the same module as the declaration. The declaration must have the internal access level modifier. A structure or class marked usableFromInline can use only types that are public or usableFromInline for its properties. An enumeration marked usableFromInline can use only types that are public or usableFromInline for the raw values and associated values of its cases.
Like the public access level modifier, this attribute exposes the declaration as part of the module’s public interface. Unlike public, the compiler doesn’t allow declarations marked with usableFromInline to be referenced by name in code outside the module, even though the declaration’s symbol is exported. However, code outside the module might still be able to interact with the declaration’s symbol by using runtime behavior.
Declarations marked with the inlinable attribute are implicitly usable from inlinable code. Although either inlinable or usableFromInline can be applied to internal declarations, applying both attributes is an error.
I want to implement a function in a dll that accepts a record as a parameter and this record as a few fields that hold pointers to callback routines. Would this be safe?
Yes, it's perfectly safe to have a pointer to a record that holds other pointers.
Your title mentions methods, though. DLLs rarely request method pointers because method pointers only exist in Delphi and C++ Builder. DLLs written expecting other tools to be able to use them will request ordinary function pointers, so please beware that method pointers are not compatible with pointers to standalone subroutines. The compiler will usually balk if you try to mix them, but type-casting can quell that error message. As a rule of thumb, if you're type-casting a function pointer or method pointer, you're doing something wrong. If your type declarations and your function declarations are correct, you won't need to type-cast.
Likewise, if you're using the # operator to create a function pointer or method pointer, you're probably doing it wrong. In most cases, the compiler can detect and assign compatible code pointers automatically, without you telling it that there's a pointer. Using # may suppress some of Delphi's type checking.
I don't see why not. I think all the usual issues with procedure/method pointers apply, so the object needs to exist if it's a method pointer.
It is safe but there are two issues that you should be aware about :
Records that declared as local variables are stored in the stack and they go away when the function returns. You should consider to allocate/dispose them on the heap with new/dispose functions.
If the DLL will be used by a program developed in other than delphi (or maybe even different versions of delpi), you have to use packed records.
How do I do things in a constructor in F#? I cant quite figure it out...
I would check out Constructors (F#):
Objects of class types have
constructors. There are two kinds of
constructors. One is the primary
constructor, whose parameters appear
in parentheses just after the type
name. You specify other, optional
additional constructors by using the
new keyword. Any such additional
constructors must call the primary
constructor.