炫耀D的超能力
作者:伊森.沃森(Ethan Watson)
为什么用d?
序号 | 特点 |
---|---|
1 | 节约成本 |
2 | 易于从C++/C# 中学习 |
3 | 很容易写干净代码 |
4 | 内省鼓励可维护性 |
5 | 但未广泛使用 |
解析模板参数
常见代码问题:
void SomeFunc( bool ImportantParam, bool AnotherImportantParam );
SomeFunc( false, true );
常见方法:
enum ImportantParam { Off, On }
enum AnotherImportantParam { Off, On }
void SomeFunc( ImportantParam p1, AnotherImportantParam p2 );
SomeFunc( ImportantParam.Off, AnotherImportantParam.On );
D
有库方案:
import std.typecons;
alias ImportantParam = Flag!"ImportantParam";
alias AnotherImportantParam = Flag!"AnotherImportantParam";
SomeFunc( ImportantParam.no, AnotherImportantParam.yes );
但模板呢?
struct SomeStruct( ImportantParam p1, AnotherImportantParam p2 )
{
static if( p1 == ImportantParam.yes )
{
int SomeImportantInt;
}
static if( p2 == AnotherImportantParam.yes )
{
float SomeImportantFloat;
}
}
//1
struct SomeStruct( ImportantParam p1 = ImportantParam.no,
AnotherImportantParam p2 = AnotherImportantParam.no )
{
static if( p1 == ImportantParam.yes )
{
int SomeImportantInt;
}
static if( p2 == AnotherImportantParam.yes )
{
float SomeImportantFloat;
}
}
解析模板参数:
struct SomeStruct( ParameterList... )
{
}
alias DodgyStruct = SomeStruct!( SomeType );
alias DodgyStruct2 = SomeStruct!( 42.0f );
alias DodgyStruct3 = SomeStruct!( SomeFunc );
alias ReallyDodgyStruct = SomeStruct!( int, 42.0f, SomeFunc );
这儿:
struct SomeStruct( DefaultOverrides... )
if( DefaultOverrides.length >= 0 )
{
// 这里解析参数
// 但如何?
static foreach( Override; DefaultOverrides )
{
// 很干净
}
}
接着:
import std.typecons;
alias ImportantParam = Flag!"ImportantParam";
alias AnotherImportantParam = Flag!"AnotherImportantParam";
alias DodgyStruct = SomeStruct!( ImportantParam.no,AnotherImportantParam.yes );
用枚举串:
enum Params : string
{
Important = "加整至构",
AnotherImportant = "加浮至构",
}
// alias DodgyStruct = SomeStruct!( ImportantParam.yes,AnotherImportantParam.no );
alias DodgyStruct = SomeStruct!( Params.AnotherImportantParam );
//接着
enum Params : string
{
Important = "加整至构",
AnotherImportant = "加浮至构",
}
bool[ Params ] ParamSet;//这里
提示,从枚造元组:
enum Params : string
{
Important = "加整至构",
AnotherImportant = "加浮至构",
}
alias AllParams = __traits( allMembers, Params );
pragma( msg, AllParams.stringof );
// tuple("Important", "AnotherImportant")
接着:
bool[ Params.length ] SetParams;
//参数集
foreach( ParamIndex, ParamName; AllParams )
{
// 插件
mixin( "enum ThisParam = Params." ~ ParamName ~ ";" );
if( DefaultOverrides.contains( ThisParam ) )//重载是否有该参
{
SetParams[ ParamIndex ] = true;
}
}
但!有个小问题:
alias DodgyStruct = SomeStruct!( Params.ImportantParam,Params.AnotherImportantParam );
// 生成相同代码,但完成不同类型
alias DodgyStruct = SomeStruct!( Params.AnotherImportantParam,Params.ImportantParam );
减少模板冗余
struct BitfieldFrom( T ) if( is( T == enum ) )
{
alias Members = __traits( allMembers, T );
static foreach( Index, Val; Members )
{
mixin( "@property bool " ~ Val ~ "() const"
~ "{ return IsSet[" ~ Index.stringof ~ "]; }" );
}
bool[ Members.length ] IsSet; // 可深入
this( T[] SetThese... );
}
接着:
alias ThisBitfield = BitfieldFrom!Params;
// 转为Params[]
ThisBitfield someBits = ThisBitfield( Params.Important, Params.AnotherImportant, Params.ReallyImportant );
ThisBitfield someOtherBits = ThisBitfield( Params.SomethingImportant );
来到:
struct SomeDodgyStruct( BitfieldFrom!Params TheseParams )
{
static if( TheseParams.ImportantParam )
{
int SomeImportantInt;
}
static if( TheseParams.AnotherImportantParam )
{
float SomeImportantFloat;
}
}
外部自省生成代码
先造消息:
module some.module;
struct ImportantMessage
{
int param;
}
自动收集消息:
Automated Message Gathering
// 如前
alias Members = __traits( allMembers, Params );
// 从整个模块取
alias ModuleMembers = __traits( allMembers, Module!"some.module" );
pragma( msg, ModuleMembers.stringof );
// tuple("ImportantMessage")
问题来了:
module some.module;
struct ImportantMessage
{
int param;
}
// 忽略该构,其他地方用,它不是消息
struct SomeImportantStruct
{
string arcaneknowledge;
}
用定属
// 编译时可查询,提取和操作的属性,只需要加@
@SomeType struct SomeStruct {}
@( 42.0f ) struct SomeStruct {}
@SomeFunc struct SomeStruct {}
消息类型用定属:
struct ServerMessage { }
struct ClientMessage { }
给消息加上标签:
@ServerMessage
struct ImportantMessage { bool bParam; }
@ClientMessage
struct AnotherImportantMessage { bool bParam; }
@ServerMessage @ClientMessage
struct Ping { }
@ServerMessage @ClientMessage
struct Pong { }
提示,指示用定属:
struct UDA { }
@UDA struct ClientMessage { }
@UDA struct ServerMessage { }
检查消息类型:
alias AllAttributes = __traits( getAttributes, MessageType );
//1
import std.traits : hasUDA;
enum PingIsClient = hasUDA!( Ping, ClientMessage );
enum IsClientMessage( T ) = hasUDA!( T, ClientMessage );
enum IsServerMessage( T ) = hasUDA!( T, ServerMessage );
一行取所有消息:
import std.meta : Filter;
alias ClientMessages = Filter!( IsClientMessage, ModuleMembers );
alias ServerMessages = Filter!( IsServerMessage, ModuleMembers );
提示,通过哈希取唯一标识
ulong MakeHash( string val );
enum UniqueID( T ) = T.stringof.MakeHash;
// 不要这样
enum UniqueID( T ) = fullyQualifiedName!( T ).MakeHash;
enum SomeStructID = UniqueID!SomeStruct; // HashOf( "some.module.SomeStruct" )
生成:
MessageCheck: switch( bytestream.ConsumeMessageID )
{
static foreach( Message; FromClientMessages )
{
case UniqueID!Message:
onReceived( bytestream.Deserialise!Message );
break MessageCheck;
}
default:
assert( false, "无效消息" );
break;
}
外部:
final void onReceive( ref const( Ping ) msg )
{
Pong pong;
sendMessage( pong );
}
final void onReceive( ref const( Pong ) msg )
{
writeln( "Pong!" );
}
重复行为:
final void onReceive( ref const( Ping ) msg );
final void onReceive( ref const( Pong ) msg );
继续:
class ClientServerCommon
{
final void onReceive( ref const( Ping ) msg );
final void onReceive( ref const( Pong ) msg );
}//这样不好
class ClientCommon : ClientServerCommon { }
class EditorClient : ClientCommon { }
class DebugClient : ClientCommon { }
//变成下面
interface ClientServerCommon
{
final void onReceive( ref const( Ping ) msg );
final void onReceive( ref const( Pong ) msg );
}//不好
class ClientCommon : ClientServerCommon, ClientCommon { }
class EditorClient : ClientServerCommon, ClientCommon { }
class DebugClient : ClientServerCommon, ClientCommon { }
重复行为:
mixin template PingPong()
{
final void onReceive( ref Pong msg );
final void onReceive( ref Ping msg );
int SomeTrackingVal;
}
class Client
{
mixin PingPong!();
}
行为规则:每模块一个,模块名必须为行为名小写
.
module behaviors;
@UDA struct Behavior { }
mixin template Behaviors( Names... )
{
static foreach( Name; Names )
{
mixin( "import behaviors." ~ Name.toLower ~ " : " ~ Name ~ ";" );
mixin( "mixin behaviors." ~ Name.toLower ~ "." ~ Name ~ "!();" );
}
}
乒乓行为:
module behaviors.pingpong;
@Behavior
mixin template PingPong()
{
final void onReceive( ref Pong msg );
final void onReceive( ref Ping msg );
}
行为
class Server
{
// 行为在该点是部分类
mixin Behaviors!( "PingPong",
"HandShake",
"Terminate" );
}
class EditorClient
{
mixin Behaviors!( "PingPong" );
}
特殊行为:
final void onReceive( ref Pong msg )
{
static if( IsServer )
{
// 干活
}
else static if( IsClient )
{
// 干其他活
}
}
//
module behaviors.pingpong;
@Behavior
mixin template PingPong( Mode ThisMode )
{
final void onReceive( ref Pong msg );
final void onReceive( ref Ping msg );
}//特化乒乓
特化行为:
class Client
{
// 错误,该行为插件试实例化插件
mixin Behaviors!( "PingPong!( Mode.Client )", "HandShake", "Terminate" );
}
同名模板
template SomeTemplate( T )
{
enum SomeTemplate = T.init;
}
T val = SomeTemplate!T;
//
template SomeTemplate( T )
{
T SomeTemplate( string SaySomething )
{
writeln( SaySomething );
return T.init;
}
}
T val = SomeTemplate!T( "赶快,好东西" );
减少模板冗余:
struct SomeDodgyStruct( BitfieldFrom!Params TheseParams )
{
static if( TheseParams.ImportantParam )
{
int SomeImportantInt;
}
static if( TheseParams.AnotherImportantParam )
{
float SomeImportantFloat;
}
}
类似:
enum IsParam( alias P ) = is( typeof( P ) == Params );
template SomeDodgyStruct( TheseParams... ) if( TheseParams.length >= 1 )
{
alias ActualParams = Filter!( IsParam, TheseParams );//实际参数
alias ParamBits = BitfieldFrom!Params;
//参数化
alias SomeDodyStruct = SomeDodgyStruct!( ParamBits( [ ActualParams ] ) );//构建
}
接着:
alias DodgyStruct = SomeStruct!( Params.ImportantParam, Params.AnotherImportantParam );
// 感谢同名模板,生成相同代码
alias DodgyStruct = SomeStruct!( Params.AnotherImportantParam, Params.ImportantParam );
同名插件模板
template PingPong( T )
{
@Behavior
mixin template PingPong()
{
//手动实例化为PingPong!(Mode.Client)!();
}
}
接着:
template PingPong( T )
{
@Behavior
mixin template PingPong()
{
// 错误,未定义T符号.插件不知道周围模板
T SomeValue;
}
}
串插件
int SomeVal;
//下面生成:
mixin( "int SomeVal;" );
//同样生成:
enum PartOne = "int ";
enum PartTwo = "SomeVal;";
mixin( PartOne ~ PartTwo );
//同样:
string MakeSomeVal( T )()
{
return T.stringof ~ " SomeVal;";
}
mixin( MakeSomeVal!int );
串插件/模板插件
都要求是合法代码
.与宏
相比,这才是维护
大量代码的正确方法
.
同名插件模板:
// 要像`PingPong!( Mode.Client )`实例化
template EponymousMixin( Params... )
{
// 新插件是另一插件的包装
mixin template EponymousMixin( )
{
mixin ActualMixinTemplate!( Params );
}
}
核心,生成插件
:
string GenerateMixin( string DesiredName, string MixinName )
{
return = "template " ~ DesiredName ~ "( Params... )"
~ "{"
~ " enum MixinString = \"mixin template " ~ DesiredName ~ "()"
~ " {"
~ " mixin " ~ MixinName ~ "!( AliasSeq!( \" ~ ToString!Params ~ \" ) );"
~ " };"
~ " mixin( MixinString );"
~ "}";
}
有了它:
module behaviors.pingpong;
@Behavior
mixin template PingPongImpl( Mode ThisMode )
{
final void onReceive( ref Pong msg );
final void onReceive( ref Ping msg );
}
mixin( GenerateMixin( "PingPong", "PingPongImpl" ) );//生成
看看:
class Client
{
// 工作很好,可读可维护,最小维护量
mixin Behaviors!( "PingPong!( Mode.Client )", "HandShake", "Terminate" );
}
同样:
@Behavior
mixin template SendReceive( alias CanSendCheck, ReceiveMessages... );
class Client
{
mixin Behaviors!( "SendReceive!( CanClientSend, FromServerMessages )" );
}
class Server
{
mixin Behaviors!( "SendReceive!( CanServerSend, FromClientMessages )" );
}
模板化用定属
早先:
MessageCheck: switch( MessageID )
{
static foreach( Message; ClientMessages )
{
case UniqueID!Message:
onReceived( bytestream.Deserialise!Message );//这里
Break MessageCheck;
}
default:
assert( false, "无效消息!" );
break;
}
序化对象
auto Deserialise( T )( ref byte[] bytestream )
{
// 如何?
}
这样:
auto Deserialise( T )( ref byte[] bytestream ) if( is( T == struct ) )
{
T output;
static foreach( Index, Member; T.tupleof )
{
output.tupleof[ Index ] = bytestream.Deserialise!( typeof( Member ) );
}
return output;
}
未序化:
@UDA struct NoSerialise { }
struct SomeStruct
{
int SerialiseThis;
@NoSerialise float IgnoreThis = 42.0f;
double AlsoSerialiseThis = 0.0f;
}
解序化:
auto Deserialise( T )( ref byte[] bytestream ) if( is( T == struct ) )
{
T output;
static foreach( Index, Member; T.tupleof )
{
static if( !hasUDA!( Member, NoSerialise ) )
{
output.tupleof[ Index ] = bytestream.Deserialise!( typeof( Member ) );
}
}
return output;
}
再访问用定属:
@SomeType struct SomeStruct {}
@( 42.0f ) struct SomeStruct {}
@SomeFunc struct SomeStruct{}
struct SomeTemplate( T )
{
T SomeValue;
}
@SomeTemplate!int struct SomeStruct {}
用模板化用定属序化勾挂
@UDA struct OnSerialise( alias Func ) { alias Call = Func; }
@UDA struct OnDeserialise( alias Func ) { alias Call = Func; }
@UDA struct OnPreSerialise( alias Func ) { alias Call = Func; }
@UDA struct OnPreDeserialise( alias Func ) { alias Call = Func; }
@UDA struct OnPostSerialise( alias Func ) { alias Call = Func; }
@UDA struct OnPostDeserialise( alias Func ) { alias Call = Func; }
距离:
struct Distance
{
@OnSerialise!( x => x.sqrt )
@OnDeserialise!( x => x * x )
double value = 0.0;
alias value this;
}
接着:
static foreach( Index, Member; T.tupleof )
{
static if( hasUDA!( Member, OnDeserialise ) )
{
output.tupleof[ Index ] = getUDAs!( Member, OnDeserialise )[ 0 ]
.Call( bytestream.Deserialise!( typeof( Member ) ) );
}
else
{
output.tupleof[ Index ] = bytestream.Deserialise!( typeof( Member ) );
}
}
距离同上.
回家研究
位字段类型和限定大小.
@UDA struct Storage( T, ulong BitCount );
enum Params : string
{
@Storage!( int, 4 )
Important = "加整至构",
@Storage!( uint, 2 )
AnotherImportant = "加浮至构",
}
用定属,命令行解析器
@DefaultArgParser( "file", "Input files", ArgIs.Repeating )
bool ParseInputFileParam( string value, int priorcallcount, ref ProgramParams
params );
@ArgParser( "o", "output", "Target file", 1.MinCalls, 1.MaxCalls )
bool ParseInputFileParam( string value, int priorcallcount, ref ProgramParams
params );
@ArgParser( "i", "iwad", "IWAD,输入文件之基",
1.MaxCalls )
bool ParseIWADParam( string value, int priorcallcount, ref ProgramParams
params );
统调,但模板:
struct Symbols( S... )
{
alias S this;
template opDispatch( string symbol );
// 神奇
}
alias TheseSymbols = Symbols!( int, float, string, bool );
pragma( msg, Symbols[ 1 ].stringof );
// 当前不行!
alias UTESFilterAlias = TheseSymbols.Filter!( IsFloat );
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现