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);
}
--------------