Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Used-Defined Builtins

49 views
Skip to first unread message

Lawrence D'Oliveiro

unread,
Feb 9, 2024, 1:05:51 AMFeb 9
to
Modula-2 had this interesting feature where, for example if you had a
pointer variable whose pointed-to objects were of type T

VAR
ptr : POINTER TO T;

and you had a statement like

ptr := NEW(T);

then the compiler would translate the “NEW(T)” into “ALLOCATE(TSIZE(T))”,
where the “ALLOCATE” function was not actually provided by the language,
but had to be defined/introduced in the current scope somehow (perhaps
IMPORTed from some implementation-provided library).

This allowed for memory allocations (and also deallocations) to be
expressed in a type-safe fashion, while still leaving the low-level
details of memory management up to some library/user code that was not an
integral part of the language implementation.

So you had a builtin function, NEW(), that could take a type as an
argument and return a result of the appropriate pointer type, which a
normal user-defined function could not do, but the compiler would delegate
the main work to a lower-level function that didn’t need to know anything
about the language type system, it only needed to know how much memory to
allocate, and would return an untyped pointer, which the compiler would
then take care of turning into a pointer of the required type.

C could benefit from some similar high-level+low-level layering like this,
don’t you think?

Kaz Kylheku

unread,
Feb 9, 2024, 3:08:53 AMFeb 9
to
On 2024-02-09, Lawrence D'Oliveiro <l...@nz.invalid> wrote:
> Modula-2 had this interesting feature where, for example if you had a
> pointer variable whose pointed-to objects were of type T
>
> VAR
> ptr : POINTER TO T;
>
> and you had a statement like
>
> ptr := NEW(T);

[ ... ]

> C could benefit from some similar high-level+low-level layering like this,
> don’t you think?

Yes, someone thought that (and other things) and came up with C++.

T *p = new T;

If T is a class, it can have its own allocator. The new operator
ensures that the constructor is called to initialize the T object.
You can pass constructor parameters to choose different constructor
overloads:

T *p = new T(4, "blah");

--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazi...@mstdn.ca

David Brown

unread,
Feb 9, 2024, 3:17:29 AMFeb 9
to
On 09/02/2024 09:08, Kaz Kylheku wrote:
> On 2024-02-09, Lawrence D'Oliveiro <l...@nz.invalid> wrote:
>> Modula-2 had this interesting feature where, for example if you had a
>> pointer variable whose pointed-to objects were of type T
>>
>> VAR
>> ptr : POINTER TO T;
>>
>> and you had a statement like
>>
>> ptr := NEW(T);
>
> [ ... ]
>
>> C could benefit from some similar high-level+low-level layering like this,
>> don’t you think?
>
> Yes, someone thought that (and other things) and came up with C++.
>
> T *p = new T;
>
> If T is a class, it can have its own allocator. The new operator
> ensures that the constructor is called to initialize the T object.
> You can pass constructor parameters to choose different constructor
> overloads:
>
> T *p = new T(4, "blah");
>

You can also provide a new global "new" operator if you want.

Making a "safer" replacement, alternative or enhancement to C was one of
the motivations for C++. Perhaps unfortunately, you can write C++ as
unsafely as you want - but if you decide to put in the effort it has the
features to make your code a great deal safer than plain old C.

Thiago Adams

unread,
Feb 9, 2024, 10:03:27 AMFeb 9
to

bart

unread,
Feb 9, 2024, 10:36:19 AMFeb 9
to
It's not particularly high level if all it does is allocate a possibly
uninitialised block of memory of a suitable size.

You will probably still need to deallocate manually or rely on a GC.
(Can you do F(NEW(T), NEW(T), NEW(T)?)

Where T has a variable size attached to it, for example ARRAY OF U, then
NEW will need to know the size, but also, this needs to be somehow
associated with that instance of T.

(And can U here itself be something you'd call NEW on?)

It's hard to retrofit such features into a lower-level language. In C
you can do:

ptr = malloc(sizeof(*ptr))

but it is not checked by the compiler. (I assume Modular 2 will complain
if you do 'ptr := NEW(U)'.)

Keith Thompson

unread,
Feb 9, 2024, 11:21:10 AMFeb 9
to
Thiago Adams <thiago...@gmail.com> writes:
[...]
> This is C23 code
>
>
> #include <stdlib.h>
> #include <string.h>
>
> static inline void* allocate_and_copy(void* s, size_t n) {
> void* p = malloc(n);
> if (p) {
> memcpy(p, s, n);
> }
> return p;
> }
>
> #define NEW(...) (typeof(__VA_ARGS__)*)
> allocate_and_copy(&(__VA_ARGS__), sizeof(__VA_ARGS__))
> #pragma expand NEW
>
> struct X {
> const int i;
> };
>
> int main() {
> auto p = NEW((struct X) {});
> }

The "#define NEW" line was wrapped, probably by your newsreader.
Either join the lines or add a backslash on the "#define" line.

"#pragma expand" is non-standard. I think it's specific to "cake", a
tool that translates C23 to earlier versions of C
(https://github.com/thradams/cake/).

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Medtronic
void Void(void) { Void(); } /* The recursive call of the void */

David Brown

unread,
Feb 9, 2024, 11:29:28 AMFeb 9
to
On 09/02/2024 16:03, Thiago Adams wrote:

> This is C23 code
>
>
> #include <stdlib.h>
> #include <string.h>
>
> static inline void* allocate_and_copy(void* s, size_t n) {
>     void* p = malloc(n);
>     if (p) {
>         memcpy(p, s, n);
>     }
>     return p;
> }
>
> #define NEW(...) (typeof(__VA_ARGS__)*)
> allocate_and_copy(&(__VA_ARGS__), sizeof(__VA_ARGS__))
> #pragma expand NEW
>
> struct X {
>     const int i;
> };
>
> int main() {
>     auto p = NEW((struct X) {});
> }
>

"#pragma expand" is not standard C. What does it mean, and what tools
support it?



Janis Papanagnou

unread,
Feb 11, 2024, 9:32:07 PMFeb 11
to
On 09.02.2024 07:05, Lawrence D'Oliveiro wrote:
> Modula-2 had this interesting feature where, for example if you had a
> pointer variable whose pointed-to objects were of type T
>
> VAR
> ptr : POINTER TO T;
>
> and you had a statement like
>
> ptr := NEW(T);
>
> then the compiler would translate the “NEW(T)” into “ALLOCATE(TSIZE(T))”,
> [...]

The type bound pointer type was already introduced by Niklaus Wirth
in Pascal (with an only slightly different syntax, type PT = ^T ),
so it's not too surprising that he used that concept also in Modula.

>
> C could benefit from some similar high-level+low-level layering like this,
> don’t you think?

Yes. Type bound pointers have been said (I think by F. L. Bauer)
to be sort of a tamed/controlled version of an inherently insecure
(pointer-)concept. I'm not sure, though, (and too tired to ponder
about it) but I seem to recall that with that coupling there's the
possibility to write type-safe pointer constructs; that would have
(IIRC) to be supported by the compiler. So introducing it in C as a
layer on top would probably not suffice to gain the same safety.
Hiding only the allocation would obviously be just syntactic sugar.

You find that binding also in C++ (as has been mentioned elsethread)
but that had been borrowed from Simula: REF(T) p; p :- new T; Both
languages allow to assign subtypes in a class hierarchy, of course,
to make most sense.

Janis

Thiago Adams

unread,
Feb 11, 2024, 11:35:00 PMFeb 11
to
Em 2/9/2024 1:20 PM, Keith Thompson escreveu:
> Thiago Adams <thiago...@gmail.com> writes:
> [...]
>> This is C23 code
>>
>>
>> #include <stdlib.h>
>> #include <string.h>
>>
>> static inline void* allocate_and_copy(void* s, size_t n) {
>> void* p = malloc(n);
>> if (p) {
>> memcpy(p, s, n);
>> }
>> return p;
>> }
>>
>> #define NEW(...) (typeof(__VA_ARGS__)*)
>> allocate_and_copy(&(__VA_ARGS__), sizeof(__VA_ARGS__))
>> #pragma expand NEW
>>
>> struct X {
>> const int i;
>> };
>>
>> int main() {
>> auto p = NEW((struct X) {});
>> }
>
> The "#define NEW" line was wrapped, probably by your newsreader.
> Either join the lines or add a backslash on the "#define" line.
>
> "#pragma expand" is non-standard. I think it's specific to "cake", a
> tool that translates C23 to earlier versions of C
> (https://github.com/thradams/cake/).
>

Exactly.
I forgot to remove pragma expand from the sample.
But it can be removed and the code still works.



Thiago Adams

unread,
Feb 11, 2024, 11:37:39 PMFeb 11
to

Thiago Adams

unread,
Feb 11, 2024, 11:44:52 PMFeb 11
to
Initially in cake, new was an operator.
The reason it was removed is because I didn't find a good way of inform
the "allocator" function.

In C++ for instance, new is very complicated with several overrides.

The open problem is how to allocate an object in heap that have constant
members.

struct X {
const int type;
};

how to create struct X on heap?

this macro solves this problem.

I am not using this macro in production.(maybe because I am not using c23)





0 new messages