d编译时反射
原文
编译时反射
使D
元编程灵活而强大.
找出表达式的有效性:
__traits( compiles, a + b );
is( typeof( a + b ) );
__traits(compiles,expr)
和is(typeof(expr))
都需要词法有效表达式
.但后者不检查是否编译
,而是检查是否存在表达式类型
.
任务:创建接受与包含与数字"相似
“的元素的数组”相似
"对象,返回平均值(数学期望)函数
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 ) ); //加减乘除
}
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
{//平均.
auto ret = arr[0] - arr[0];
foreach( i; 0 .. arr.length )
ret = ret + arr[i];
return ret * ( 1.0f / arr.length );
}
用法:
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( "仅3元素,不要越界" );
}
}
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) ); //
}
//细节不足,不要使用.
is(…)
is
构造很强大.
is( T );
接下来,检查T类型:
is( T == Type );
is( T : Type );
//新别名
is( T ident : Type );
is( T ident == Type );
示例:
void foo(T)( T value )
{
static if( is( T U : long ) ) //
alias Num = U; //
else
alias Num = long; //
}
还可
is( T == 限定 );
这里,限定
是:struct,union,class,interface,enum,function,delegate,const,immutable,shared
等.因此,检查T是否是结构,联合,类
等.
还可结合检查和别名:
is( T ident == 限定 )
另一个有趣技巧:模式匹配
类型
is( T == TypeTempl, TemplParams... );
is( T : TypeTempl, TemplParams... );
is( T ident == TypeTempl, TemplParams... );
is( T ident : TypeTempl, TemplParams... );
此时,TypeTempl
是描述(复合)类型,TemplParams
是构成TypeTempl
的元素.
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一样的构: ", 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, "关联数组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, "静态数组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 );
}
输出:
Foo样构: Foo!(10LU, double)
S: Foo(ulong N, T) if (N > 0)
N: 10LU
T: double
Foo样构: Bar!(12LU, string)
S: Bar(ulong N, T) if (N > 0)
N: 12LU
T: string
关联数组T[X]: int[string]
T(value): int
X(key): string
关联数组T[X]: string[int]
T(value): string
X(key): int
静态数组T[N]: short[8]
T(value): short
N(length): 8LU
other: int
__traits(keyWord, …)
项 | 意思 |
---|---|
compiles | 表达式是否有效 |
isAbstractClass | 抽象类 |
isArithmetic | 算术类型(数字和枚举) |
isAssociativeArray | 关联数组 |
isFinalClass | 终类(不能继承) |
isPOD | 旧数据 可通过简单逐字节复制 来初化类型(隐藏字段 ,禁止使用析构函数 ) |
isNested | 嵌套类型(依赖于上下文) |
isFloating | 浮点数(包括复数) |
isIntegral | 整数 |
isScalar | 标量类型(数字,枚举,指针 ),__vector(int[4]) 也是标量类型 |
isStaticArray | 静态数组 |
isUnsigned | 正 |
isVirtualMethod | 虚方法 |
isVirtualFunction | 虚函数 |
isAbstractFunction | 抽象函数 |
isFinalFunction | 终函数 |
isStaticFunction | 静态函数 |
isOverrideFunction | 重载函数 |
isRef | 引用参数 |
isOut | 输出 参数 |
isLazy | 惰参数(按需求值) |
isSame | 表达式是否相同 |
hasMember | 类/构 是否有字段/方法 ,第一个参数是类型 (或类型对象 ),第二个参数是字段/方法名 串. |
示例:
class A { class B {} }
pragma(msg, __traits(isNested,A.B));
//是嵌套
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
关于isFunction和isVirtualMethod和isVirtualFunction的区别
为了清楚起见,我写了个测试来显示差异
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) );
}
结果:
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
对可重载或已重载
内容都返回true
.如果未重载
函数,而原本是final
的,则不是虚方法,而是虚函数.
我无法解释λ
和函数(函数类型字面)
附近的问号,它们未通过函数或委托
测试.
后面的,提供示例,具体见文档:
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 );
}
//结果
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(第1个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
}
模板和签名约束
模板函数最简单形式如下:
void func(T)( T val ) { ... }
但是模板参数
也有形式,比如is
构造,用于检查隐式转换
,甚至用于匹配模式
.结合签名约束
,可创建有趣的重载模板函数
组合:
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( "带串键AA" ); }
void func(T: U[E], U, E)( T val ) if( is( E : long ) ) { writeln( "带数字键AA" ); }
void main()
{
func( 120 ); // 数字
func( ["hello": 12] ); // 串AA
func( [10: 12] ); // 数字AA
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现