当前位置:网站首页>D compile time reflection

D compile time reflection

2022-06-25 05:49:00 fqbqrr

original text
Compile time reflection send D Metaprogramming is flexible and powerful .
Find out the validity of the expression :

__traits( compiles, a + b );
is( typeof( a + b ) );

__traits(compiles,expr) and is(typeof(expr)) Both need lexical validity expression . But the latter does not check whether compile , Instead, check for the presence of Expression type .
Mission : Create accept and include with numbers " be similar “ An array of elements ” be similar " object , Return average ( Mathematical expectation ) function

template isNumArray(T)
{
    
    enum isNumArray = __traits(compiles,
    {
    
        auto a = T.init[0]; 

        static if( !__traits(isArithmetic,a) ) // 
        {
    
            static assert( __traits( compiles, a=a+a ) ); //
            static assert( __traits( compiles, a=a-a ) ); //
            static assert( __traits( compiles, a=a*.0f ) ); // Add, subtract, multiply and divide 
        }

        auto b = T.init.length; 
        static assert( is( typeof(b) : size_t ) );
    });
}

auto mean(T)( T arr ) @property if( isNumArray!T )
in {
     assert( arr.length > 0 ); } body
{
    // Average .
    auto ret = arr[0] - arr[0];
    foreach( i; 0 .. arr.length )
        ret = ret + arr[i]; 
    return ret * ( 1.0f / arr.length );
}

usage :

import std.string : format;

struct Vec2
{
    
    float x=0, y=0;
    auto opBinary(string op)( auto ref const Vec2 rhs ) const
        if( op == "+" || op == "-" )
    {
     mixin( format( "return Vec2( x %1$s rhs.x, y %1$s rhs.y );", op ) ); }

    auto opBinary(string op)( float rhs ) const
        if( op == "*" )
    {
     return Vec2( x * rhs, y * rhs ); }
}

struct Triangle
{
    
    Vec2 p1, p2, p3;

    auto opIndex(size_t v)
    {
    
        switch(v)
        {
    
            case 0: return p1;
            case 1: return p2;
            case 2: return p3;
            default: throw new Exception( " only 3 Elements , Don't cross the line " );
        }
    }

    static pure size_t length() {
     return 3; }
}

void main()
{
    
    auto f = [ 1.0f, 2, 3 ];
    assert( f.mean == 2.0f ); // 

    auto v = [ Vec2(1,6), Vec2(2,7), Vec2(3,5) ];
    assert( v.mean == Vec2(2,6) ); // 

    auto t = Triangle( Vec2(1,6), Vec2(2,7), Vec2(3,5) );
    assert( t.mean == Vec2(2,6) ); // 
}
// Lack of detail , Do not use .

is(…)

is The structure is very powerful .

is( T );

Next , Check T type :

is( T == Type );
is( T : Type );
// New alias 
is( T ident : Type );
is( T ident == Type );

Example :

void foo(T)( T value )
{
    
    static if( is( T U : long ) ) // 
        alias Num = U; //
    else
        alias Num = long; // 
}

Still can

is( T ==  limit  );

here , limit yes :struct,union,class,interface,enum,function,delegate,const,immutable,shared etc. . therefore , Check T Whether it is structure , union , class etc. .
You can also combine checks with aliases :

is( T ident ==  limit  )

Another interesting trick : Pattern matching type

is( T == TypeTempl, TemplParams... );
is( T : TypeTempl, TemplParams... );

is( T ident == TypeTempl, TemplParams... );
is( T ident : TypeTempl, TemplParams... );

here ,TypeTempl Is to describe ( Reunite with ) type ,TemplParams It's the composition TypeTempl The elements of .

struct Foo(size_t N, T) if( N > 0 ) {
     T[N] data; }
struct Bar(size_t N, T) if( N > 0 ) {
     float[N] arr; T value; }

void func(U)( U val )
{
    
    static if( is( U E == S!(N,T), alias S, size_t N, T ) )
    {
    
        pragma(msg, "Foo The same structure : ", E );
        pragma(msg, "S: ", S.stringof);
        pragma(msg, "N: ", N);
        pragma(msg, "T: ", T);
    }
    else static if( is( U T : T[X], X ) )
    {
    
        pragma(msg, " Associative array T[X]: ", U );
        pragma(msg, "T(value): ", T);
        pragma(msg, "X(key): ", X);
    }
    else static if( is( U T : T[N], size_t N ) )
    {
    
        pragma(msg, " Static array T[N]: ", U );
        pragma(msg, "T(value): ", T);
        pragma(msg, "N(length): ", N);
    }
    else pragma(msg, "other: ", U );
    pragma(msg,"");
}

void main()
{
    
    func( Foo!(10,double).init );
    func( Bar!(12,string).init );
    func( [ "hello": 23 ] );
    func( [ 42: "habr" ] );
    func( Foo!(8,short).init.data );
    func( 0 );
}

Output :

Foo Like structure : Foo!(10LU, double)
S: Foo(ulong N, T) if (N > 0)
N: 10LU
T: double

Foo Like structure : Bar!(12LU, string)
S: Bar(ulong N, T) if (N > 0)
N: 12LU
T: string

 Associative array T[X]: int[string]
T(value): int
X(key):   string

 Associative array T[X]: string[int]
T(value): string
X(key):   int

 Static array T[N]: short[8]
T(value):  short
N(length): 8LU

other: int

__traits(keyWord, …)

term Meaning
compiles Whether the expression is valid
isAbstractClass abstract class
isArithmetic Arithmetic Type ( Numbers and enumerations )
isAssociativeArray Associative array
isFinalClass End class ( Cannot inherit )
isPOD Old data This can be done by simply Copy byte by byte To initialize the type ( Hide fields , No use Destructor )
isNested Nested Type ( Dependent on context )
isFloating Floating point numbers ( Including the plural )
isIntegral Integers
isScalar Scalar type ( Numbers , enumeration , The pointer ),__vector(int[4]) It is also a scalar type
isStaticArray Static array
isUnsigned just
isVirtualMethod Virtual method
isVirtualFunction Virtual functions
isAbstractFunction Abstract functions
isFinalFunction Final function
isStaticFunction Static functions
isOverrideFunction overloaded function
isRef reference parameter
isOut Output Parameters
isLazy Idle parameter ( By demand value )
isSame Whether the expressions are the same
hasMember class / structure Is there a Field / Method , The first parameter is type ( or Type object ), The second parameter is Field / Method name strand .

Example :

class A {
     class B {
    } }
pragma(msg, __traits(isNested,A.B));
// It's nesting 
void f1()
{
    
    auto f2() {
     return 12; }
    pragma(msg,__traits(isNested,f2)); // true
}

auto f1()
{
    
    auto val = 12;
    struct S {
     auto f2() {
     return val; } } 
    return S.init;
}
pragma(msg,__traits(isNested,typeof(f1()))); // true
//
struct Foo {
     float value; }
pragma(msg, __traits(hasMember, Foo, "value")); // true
pragma(msg, __traits(hasMember, Foo, "data")); // false

About isFunction and isVirtualMethod and isVirtualFunction The difference between

For the sake of clarity , I wrote a test to show the difference

import std.stdio, std.string;

string test(alias T)()
{
    
    string ret;
    ret ~= is( typeof(T) == delegate ) ? "D " :
           is( typeof(T) == function ) ? "F " : "? "; 
    ret ~= __traits(isVirtualMethod,T)    ? "m|" : "-|";
    ret ~= __traits(isVirtualFunction,T)  ? "v|" : "-|";
    ret ~= __traits(isAbstractFunction,T) ? "a|" : "-|";
    ret ~= __traits(isFinalFunction,T)    ? "f|" : "-|";
    ret ~= __traits(isStaticFunction,T)   ? "s|" : "-|";
    ret ~= __traits(isOverrideFunction,T) ? "o|" : "-|";
    return ret;
}

class A
{
    
    static void stat() {
    }
    void simple1() {
    }
    void simple2() {
    }
    private void simple3() {
    }
    abstract void abstr() {
    }
    final void fnlNOver() {
    }
}

class B : A
{
    
    override void simple1() {
    }
    final override void simple2() {
    }
    override void abstr() {
    }
}

class C : B
{
    
    final override void abstr() {
    }
}

interface I
{
    
    void abstr();
    final void fnl() {
    }
}

struct S {
     void func(){
    } }

void globalFunc() {
    }

void main()
{
    
    A a; B b; C c; I i; S s;
    writeln( " id T m|v|a|f|s|o|" );
    writeln( "--------------------------" );
    writeln( " lambda: ", test!(x=>x) );
    writeln( " function: ", test!((){
     return 3; }) );
    writeln( " delegate: ", test!((){
     return b; }) );
    writeln( " s.func: ", test!(s.func) );
    writeln( " global: ", test!(globalFunc) );
    writeln( " a.stat: ", test!(a.stat) );
    writeln( " a.simple1: ", test!(a.simple1) );
    writeln( " a.simple2: ", test!(a.simple2) );
    writeln( " a.simple3: ", test!(a.simple3) );
    writeln( " a.abstr: ", test!(a.abstr) );
    writeln( "a.fnlNOver: ", test!(a.fnlNOver) );
    writeln( " b.simple1: ", test!(b.simple1) );
    writeln( " b.simple2: ", test!(b.simple2) );
    writeln( " b.abstr: ", test!(b.abstr) );
    writeln( " c.abstr: ", test!(c.abstr) );
    writeln( " i.abstr: ", test!(i.abstr) );
    writeln( " i.fnl: ", test!(i.fnl) );
}

result :

        id  T m|v|a|f|s|o|
--------------------------
    lambda: ? -|-|-|-|-|-|
  function: ? -|-|-|-|s|-|
  delegate: D -|-|-|-|-|-|
    s.func: F -|-|-|-|-|-|
    global: F -|-|-|-|s|-|
    a.stat: F -|-|-|-|s|-|
 a.simple1: F m|v|-|-|-|-|
 a.simple2: F m|v|-|-|-|-|
 a.simple3: F -|-|-|-|-|-|
   a.abstr: F m|v|a|-|-|-|
a.fnlNOver: F -|v|-|f|-|-|
 b.simple1: F m|v|-|-|-|o|
 b.simple2: F m|v|-|f|-|o|
   b.abstr: F m|v|-|-|-|o|
   c.abstr: F m|v|-|f|-|o|
   i.abstr: F m|v|a|-|-|-|
     i.fnl: F -|-|a|f|-|-|

isVirtualMethod Yes Overloadable or overloaded All contents are returned true. If Not overloaded function , And it was final Of , Is not a virtual method , It's a virtual function .
I can't explain λ and function ( Function type literal ) Nearby question marks , They failed Function or delegate test .
hinder , Provide examples , See the document for details :

enum Foo;
class Bar {
     @(42) @Foo void func() pure @nogc @property {
    } } 
pragma(msg, __traits(getAttributes, Bar.func)); // 
@?Foo float value;
pragma(msg, __traits(getAttributes, value)); // 
//
enum Foo;
class Bar {
     @(42) @Foo void func() pure @nogc @property {
    } } 
pragma(msg, __traits(getFunctionAttributes, Bar.func)); 
//
class Bar {
     float value; }
Bar bar; 
__traits(getMember, bar, "value") = 10; 
//
import std.stdio;

class A
{
    
    void foo( float ) {
    }
    void foo( string ) {
    }
    int foo( int ) {
     return 12; }
}

void main()
{
    
    foreach( f; __traits(getOverloads, A, "foo") )
        writeln( typeof(f).stringof );
}
// result 
void(float _param_0)
void(string _param_0)
int(int _param_0)
//
class A
{
    
    float val1; 
    A val2; 
    void* val3; 
    void[] val4; 
    void function() val5; 
    void delegate() val6; 
}

enum bm = 
0b101011000;
//||||||||+- 
//|||||||+-- 
//||||||+--- float val1
//|||||+---- A val2
//||||+----- void* val3
//|||+------ void[] val4 
//||+------- void[] val4 
//|+-------- void function() val5 
//+--------- void delegate() val6 
//0---------- void delegate() val6( The first 1 individual 0)
static assert( __traits(getPointerBitmap,A) == [10*size_t.sizeof, bm] );

struct B {
     float x, y, z; }
static assert( __traits(getPointerBitmap,B) == [3*float.sizeof, 0] ); 
//
import std.stdio;

struct B
{
    
    float value;
    void func() {
    }
}

alias F = B.func;

void main()
{
    
    writeln( __traits(parent,writeln).stringof ); // module stdio
    writeln( typeid( typeof( __traits(parent,F).value ) ) ); // float
}

Template and signature constraints

The simplest form of a template function is as follows :

void func(T)( T val ) {
     ... }

however Template parameter There are also forms , such as is structure , Used for inspection Implicit conversion , Even for Matching mode . combination Signature constraints , Create interesting Overload template function Combine :

import std.stdio;

void func(T:long)( T val ) {
     writeln( "number" ); }
void func(T: U[E], U, E)( T val ) if( is( E == string ) ) {
     writeln( " With string key AA" ); }
void func(T: U[E], U, E)( T val ) if( is( E : long ) ) {
     writeln( " With numeric keys AA" ); }

void main()
{
    
    func( 120 ); //  Numbers 
    func( ["hello": 12] ); //  strand AA 
    func( [10: 12] ); //  Numbers AA 
}
原网站

版权声明
本文为[fqbqrr]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202201256443523.html