On Monday, 28 August 2017 at 14:27:19 UTC, Jacob Carlborg wrote:
I'm working on some code that sanitizes and converts values of different types to strings. I thought it would be a good idea to wrap the sanitized string in a struct to have some type safety. Ideally it should not be possible to create this type without going through the sanitizing functions.

The problem I have is that I would like these functions to push up the allocation decision to the caller. Internally these functions use formattedWrite. I thought the natural design would be that the sanitize functions take an output range and pass that to formattedWrite.

[...]

Any suggestions how to fix this or a better idea?

If you want the caller to be just in charge of allocation, that's what std.experimental.allocator provides. In this case, I would polish up the old "format once to get the length, allocate, format second time into allocated buffer" method used with snprintf for D:

--- test.d ---
import std.stdio;
import std.experimental.allocator;

struct CountingOutputRange
{
private:
        size_t _count;
public:
        size_t count() { return _count; }
        void put(char c) { _count++; }
}

char[] sanitize(string value, IAllocator alloc)
{
        import std.format : formattedWrite, sformat;

        CountingOutputRange r;
        (&r).formattedWrite!"'%s'"(value); // do not copy the range

        auto s = alloc.makeArray!char(r.count);
        scope (failure) alloc.dispose(s);

// This should only throw if the user provided allocator returned less
        // memory than was requested
        return s.sformat!"'%s'"(value);
}

void main()
{
        auto s = sanitize("foo", theAllocator);
        scope (exit) theAllocator.dispose(s);
        writeln(s);
}
--------------

Reply via email to