This is a useful technique, and non-obvious enough that other D
community members were surprised when I showed it to them.
The unusual formatting and code layout used here achieves the following
goals:
1. Have this example appear below "Basic usage".
2. Have the overload set appear as it would at module level.
3. Have as much of the example code unit-tested as possible.
Goal (2), in particular, rules out the use of a "wrapper" struct to
create an overload set of static methods, which is the technique that's
normally used to include an overload set in a unittest block.
This technique has turned out not to be very useful in practice, and the
example is a bit subtle and tricky to understand. Removing it will make
room for more useful and less confusing examples.
In an attempt make it so that non-copyable types worked with some of the
functions in std/range/package.d, they were made to use moves instead of
assignment, which broke the code for types which work with assignment
but not moves (which affected the folks at Weka).
The code checked for assignment but not whether move could be used, and
that didn't change when the code was changed to use move, meaning that
the checks didn't match what the code was actually doing.
So, to support both the non-copyable types and the ones that can be
assigned to but not moved to, this changes the code to use
core.lifetime.forward which will move the argument if it can and assign
otherwise. So ,the code that worked previously should work again, and
the newer functionality of being able to use non-copyable types with
this code should continue to work.
Discussion here: https://github.com/dlang/phobos/pull/8721
std.process.Config.preExecDelegate is just like
std.process.Config.preExecFunction, but can capture an environment, for
example:
import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl;
import std.process : Config, execute;
void runProgram(int pdeathsig)
{
execute(
["program"],
config: Config(
preExecDelegate: () @trusted =>
prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1,
),
);
}
preExecFunction is retained for backwards compatibility. If both
preExecFunction and preExecDelegate are given, both are called.
std.process.Config.preExecFunction is now a delegate instead of a function
pointer, and can therefore capture an environment, for example:
import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl;
import std.process : Config, execute;
void runProgram(int pdeathsig)
{
execute(
["program"],
config: Config(
preExecFunction: () @trusted =>
prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1,
),
);
}
Despite function pointers implicitly converting to delegates, this is a
backwards-incompatible change, as user code may rely on the field being a
function pointer. For example, code like the following will no longer compile:
import std.process : Config;
nothrow pure @nogc @safe
bool f() { return true; }
void example()
{
auto config = Config(preExecFunction: &f);
bool function() g = config.preExecFunction;
}
Since the compiler is treating the auto-generated copy-constructor for
OnlyResult as private (thus rendering it useless outside of
std.range.package), this commit adds an explicit one and makes it
public. Once the dmd bug has been fixed, the explicit copy constructor
should be removed.
If there's a constructor that looks like a copy constructor except that
it takes an rvalue instead of taking the argument by ref, then that type
can't have a copy constructor, and one of Tuple's constructors was
causing that problem. So, this fixes it so that it doesn't.