1

I've read somewhere that D supports specialization of functions to calls where arguments are compile-time constants. Typical use of this is in matrix power functions (if exponent is 2 x*x is often faster than the general case).

I want this in my member function

   bool opIndexAssign(bool b, size_t i) @trusted pure nothrow in {
        assert(i < len);        // TODO: Add static assert(i < len) when i is constant
    } body {
        b ? bts(ptr, i) : btr(ptr, i);
        return b;
    }

of a statically sized BitSet struct I'm writing. This in order to, when possible, get compile-time bounds checking on the index variable i. I thought

bool opIndexAssign(bool b, const size_t i) @trusted pure nothrow in {
    static assert(i < len);
} body {
    b ? bts(ptr, i) : btr(ptr, i);
    return b;
}

would suffice but then DMD complains as follows

dmd -debug -gc -gs -unittest -D -Dd/home/per/.emacs.d/auto-builds/dmd/Debug-Boundscheck-Unittest/home/per/Work/justd/ -w -main  ~/Work/justd/bitset.d /home/per/Work/justd/assert_ex.d -of/home/per/.emacs.d/auto-builds/dmd/Debug-Boundscheck-Unittest/home/per/Work/justd/bitset
/home/per/Work/justd/bitset.d(58): Error: bitset.BitSet!2.BitSet.opIndexAssign called with argument types (bool, int) matches both:
    /home/per/Work/justd/bitset.d(49): opIndexAssign(bool b, ulong i)
and:
    /home/per/Work/justd/bitset.d(65): opIndexAssign(bool b, const(ulong) i)
/home/per/Work/justd/bitset.d(66): Error: variable i cannot be read at compile time
/home/per/Work/justd/bitset.d(66):        while evaluating: static assert(i < 2LU)
/home/per/Work/justd/bitset.d(58): Error: bitset.BitSet!2.BitSet.opIndexAssign called with argument types (bool, int) matches both:
    /home/per/Work/justd/bitset.d(49): opIndexAssign(bool b, ulong i)

Do I have to make parameter i a template parameter, say using type U, and then use static if someTypeTrait!U. I tried this but isMutable!Index always evaluates to true.

import std.traits: isIntegral;
bool opIndexAssign(Index)(bool b, Index i) @trusted pure nothrow if (isIntegral!Index) in {
    import std.traits: isMutable;
    // See also: http://stackoverflow.com/questions/19906516/static-parameter-function-specialization-in-d
    static if (isMutable!Index) {
        assert(i < len);
    } else {
        import std.conv: to;
        static assert(i < len,
                      "Index " ~ to!string(i) ~ " must be smaller than BitSet length " ~  to!string(len));
    }
} body {
    b ? bts(ptr, i) : btr(ptr, i);
    return b;
}
Nordlöw
  • 11,838
  • 10
  • 52
  • 99

1 Answers1

2

What you're trying to do doesn't really work. You can do a template value parameter:

void foo(int i)() { /* use i at compile time */ }

but then you can't pass a runtime value to it, and it has different call syntax: foo!2 vs foo(2).

The closest you can get is is CTFE:

int foo(int i) { return i; }
enum something = foo(2); // works, evaluated at compile time
int s = foo(2); // also works, but runs at runtime.

Inside the function, there is a magic if(__ctfe) { running at compile time } else { at runtime}, but again, this isn't if there's a literal, it is if the function is run in a CT context, e.g., assigning the result to an enum constant.

But, otherwise, an int literal is still a mutable int as far as the function is concerned. So what you're specifically trying to do won't work in D as it is right now. (There's been some talk about wanting a way to tell if it is a literal, but as far as I know, there's no plan to actually do it.)

Adam D. Ruppe
  • 25,382
  • 4
  • 41
  • 60