Jump to page: 1 2 3
Thread overview
A safer interface for core.stdc
Feb 08, 2015
bearophile
Feb 08, 2015
H. S. Teoh
Feb 08, 2015
H. S. Teoh
Feb 08, 2015
Vlad Levenfeld
Feb 08, 2015
H. S. Teoh
Feb 08, 2015
FG
Feb 08, 2015
John Colvin
Feb 11, 2015
Walter Bright
Feb 08, 2015
Paulo Pinto
Feb 08, 2015
tcak
Feb 08, 2015
H. S. Teoh
Feb 08, 2015
bearophile
Feb 08, 2015
Tobias Pankrath
Feb 08, 2015
Johannes Pfau
Feb 09, 2015
Dicebot
Feb 09, 2015
Dicebot
Feb 11, 2015
finalpatch
February 07, 2015
I was looking into ways to make core.stdc safer. That should be relatively easy to do by defining a few wrappers. For example:

int  setvbuf(FILE* stream, char* buf, int mode, size_t size);

is unsafe because there's no relationship between buf and size. But this is fine:

@trusted int setvbuf(T)(FILE* stream, T[] buf, int mode)
if (is(T == char) || is(T == byte) || is(T == ubyte))
{
    return setvbuf(stream, cast(char*) buf.ptr, mode, buf.length);
}

Another example is:

int stat(in char*, stat_t*);

which may start reading through random memory if the string is not zero-terminated. Again, the solution is here to ensure the string does have a terminating zero:

@trusted int stat(in char[] name, stat_t* p)
{
    if (isZeroTerminated(name)) return stat(name.ptr, p);
    auto t = cast(char*) malloc(name.length + 1);
    scope(exit) free(t);
    memcpy(t, name.ptr, name.length);
    t[name.length] = 0;
    return stat(t, p);
}

Such wrappers would allow safe code to use more C stdlib primitives. The question is whether these wrappers are worth adding to core.stdc.stdio.


Thanks,

Andrei
February 08, 2015
Andrei Alexandrescu:

> Such wrappers would allow safe code to use more C stdlib primitives.

I'd also like a safer templated wrapper for calloc() and malloc() and similar.

Bye,
bearophile
February 08, 2015
On Sun, Feb 08, 2015 at 12:39:39AM +0000, bearophile via Digitalmars-d wrote:
> Andrei Alexandrescu:
> 
> >Such wrappers would allow safe code to use more C stdlib primitives.
> 
> I'd also like a safer templated wrapper for calloc() and malloc() and
> similar.
[...]

You mean something like this?

	T* malloc(T)() @trusted
	{
		return cast(T*)malloc(T.sizeof);
	}

	struct MyStruct {
		int x, y, z;
	}

	void main() {
		auto p = malloc!MyStruct();

		// Not sure how to make free() usable from @safe, unless
		// we wrap the pointer returned by malloc().
		free(p);
	}


T

-- 
Leather is waterproof.  Ever see a cow with an umbrella?
February 08, 2015
On 2/7/15 5:26 PM, H. S. Teoh via Digitalmars-d wrote:
> On Sun, Feb 08, 2015 at 12:39:39AM +0000, bearophile via Digitalmars-d wrote:
>> Andrei Alexandrescu:
>>
>>> Such wrappers would allow safe code to use more C stdlib primitives.
>>
>> I'd also like a safer templated wrapper for calloc() and malloc() and
>> similar.
> [...]
>
> You mean something like this?
>
> 	T* malloc(T)() @trusted
> 	{
> 		return cast(T*)malloc(T.sizeof);
> 	}

I think that would go as follows:

private @system T[] mallocUninitializedArrayImpl(T)(size_t n)
{
    auto p = malloc(n * T.sizeof);
    p || assert(0, "Not enough memory");
    return (cast(T*) p)[0 .. n];
}

@trusted T[] mallocUninitializedArray(size_t n)
if (!hasIndirections!T)
{
    return mallocUninitializedArrayImpl!T(n);
}

@system T[] mallocUninitializedArray(size_t n)
if (hasIndirections!T)
{
    return mallocUninitializedArrayImpl!T(n);
}

Similarly there'd be a mallocMinimallyInitializedArray that zeroes only pointers and is @trusted for all types.

Then we'd probably have a @trusted callocArray that blasts zeros throughout. It's @trusted because we know pointers are zeroes (an assumption somewhat not robust in theory but fine in practice).

Then we'd have a mallocArray that allocates an array and initializes each element with .init.

> 	struct MyStruct {
> 		int x, y, z;
> 	}
>
> 	void main() {
> 		auto p = malloc!MyStruct();
>
> 		// Not sure how to make free() usable from @safe, unless
> 		// we wrap the pointer returned by malloc().
> 		free(p);
> 	}

Indeed we have no safe way to wrap free.


Andrei

February 08, 2015
On Saturday, 7 February 2015 at 23:50:55 UTC, Andrei Alexandrescu wrote:
> I was looking into ways to make core.stdc safer. That should be relatively easy to do by defining a few wrappers. For example:
>
> int  setvbuf(FILE* stream, char* buf, int mode, size_t size);
>
> is unsafe because there's no relationship between buf and size. But this is fine:
>
> @trusted int setvbuf(T)(FILE* stream, T[] buf, int mode)
> if (is(T == char) || is(T == byte) || is(T == ubyte))
> {
>     return setvbuf(stream, cast(char*) buf.ptr, mode, buf.length);
> }
>
> Another example is:
>
> int stat(in char*, stat_t*);
>
> which may start reading through random memory if the string is not zero-terminated. Again, the solution is here to ensure the string does have a terminating zero:
>
> @trusted int stat(in char[] name, stat_t* p)
> {
>     if (isZeroTerminated(name)) return stat(name.ptr, p);
>     auto t = cast(char*) malloc(name.length + 1);
>     scope(exit) free(t);
>     memcpy(t, name.ptr, name.length);
>     t[name.length] = 0;
>     return stat(t, p);
> }
>
> Such wrappers would allow safe code to use more C stdlib primitives. The question is whether these wrappers are worth adding to core.stdc.stdio.
>
>
> Thanks,
>
> Andrei

One of the reasons why I use C functions is that I expect same behaviour from D code what I would expect from C. I don't think it is a good idea to make wrapper on top of them. Maybe you could say, "Hey, look, it just makes safer, that's all", but, hmm there are so many functions, and this wrapping process can go in many directions.
February 08, 2015
On Sat, Feb 07, 2015 at 06:19:19PM -0800, Andrei Alexandrescu via Digitalmars-d wrote: [...]
> private @system T[] mallocUninitializedArrayImpl(T)(size_t n)
> {
>     auto p = malloc(n * T.sizeof);
>     p || assert(0, "Not enough memory");

This is a truly strange way of writing it... why not:

	assert(p !is null, "Not enough memory");

?


>     return (cast(T*) p)[0 .. n];
> }


T

-- 
Tell me and I forget. Teach me and I remember. Involve me and I understand. -- Benjamin Franklin
February 08, 2015
On Sunday, 8 February 2015 at 04:02:52 UTC, H. S. Teoh wrote:
>>     p || assert(0, "Not enough memory");
>
> This is a truly strange way of writing it... why not:
>
> 	assert(p !is null, "Not enough memory");

I think Andrei's version will remain in release builds, but yours will be elided.
February 08, 2015
On 2/7/15 7:52 PM, tcak wrote:
> One of the reasons why I use C functions is that I expect same behaviour
> from D code what I would expect from C. I don't think it is a good idea
> to make wrapper on top of them. Maybe you could say, "Hey, look, it just
> makes safer, that's all", but, hmm there are so many functions, and this
> wrapping process can go in many directions.

Just looking at making them safe. Not all can be made safe btw. -- Andrei
February 08, 2015
On 2/7/15 8:00 PM, H. S. Teoh via Digitalmars-d wrote:
> On Sat, Feb 07, 2015 at 06:19:19PM -0800, Andrei Alexandrescu via Digitalmars-d wrote:
> [...]
>> private @system T[] mallocUninitializedArrayImpl(T)(size_t n)
>> {
>>      auto p = malloc(n * T.sizeof);
>>      p || assert(0, "Not enough memory");
>
> This is a truly strange way of writing it... why not:
>
> 	assert(p !is null, "Not enough memory");
>
> ?

assert(0) is not removed in release mode. -- Andrei

February 08, 2015
On Sat, Feb 07, 2015 at 08:14:39PM -0800, Andrei Alexandrescu via Digitalmars-d wrote:
> On 2/7/15 7:52 PM, tcak wrote:
> >One of the reasons why I use C functions is that I expect same behaviour from D code what I would expect from C. I don't think it is a good idea to make wrapper on top of them. Maybe you could say, "Hey, look, it just makes safer, that's all", but, hmm there are so many functions, and this wrapping process can go in many directions.
> 
> Just looking at making them safe. Not all can be made safe btw. -- Andrei

Come to think of it, is there any point in making malloc @safe/@trusted at all? I don't think it's possible to make free() @safe, so what's the purpose of making malloc callable from @safe code? Unless you make a ref-counted wrapper of some sort around it, in which case you might as well use RefCounted instead.

I thought about making the equivalent of auto_ptr, but unless you make it non-copyable (or only destructively copyable, and no pointer extraction is permitted), there's no way it can be truly @safe.

The only possible advantage we could gain is *type* safety by wrapping malloc in a type-safe way (i.e., don't expose void*).


T

-- 
Long, long ago, the ancient Chinese invented a device that lets them see through walls. It was called the "window".
« First   ‹ Prev
1 2 3