├── .codecov.yml ├── .editorconfig ├── .gitignore ├── .travis.yml ├── .vscode └── tasks.json ├── LICENSE ├── README.md ├── dub.json └── source └── bolts ├── experimental ├── package.d ├── refraction.d └── signatures.d ├── from.d ├── internal.d ├── iz.d ├── members.d ├── meta.d ├── package.d ├── range.d └── traits ├── functions.d ├── has.d ├── package.d ├── symbols.d └── types.d /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | after_n_builds: 1 4 | 5 | coverage: 6 | precision: 3 7 | round: down 8 | range: 80...100 9 | 10 | status: 11 | project: true 12 | patch: off 13 | changes: 14 | default: 15 | threshold: 10% 16 | 17 | comment: false -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{d,json,css}] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | bolts.so 5 | bolts.dylib 6 | bolts.dll 7 | bolts.a 8 | bolts.lib 9 | bolts-test-* 10 | *.exe 11 | *.o 12 | *.obj 13 | *.lst 14 | bin/ 15 | docs/ 16 | dub.selections.json 17 | mixins.txt 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: d 2 | 3 | matrix: 4 | include: 5 | - d: dmd 6 | script: 7 | - dub test -b unittest-cov 8 | - git clone https://github.com/aliak00/ddox-dark-theme.git 9 | - mv ddox-dark-theme/docs . 10 | - dub build -b ddox 11 | addons: 12 | apt: 13 | packages: 14 | - libevent-dev 15 | - libssl-dev 16 | - pkg-config 17 | - zlib1g-dev 18 | after_success: bash <(curl -s https://codecov.io/bash) 19 | 20 | deploy: 21 | local_dir: docs 22 | provider: pages 23 | skip_cleanup: true 24 | github_token: $GITHUB_TOKEN 25 | on: 26 | branch: master 27 | tags: true 28 | 29 | cache: 30 | directories: 31 | - $HOME/.dub 32 | 33 | - d: dmd 34 | os: osx 35 | - d: ldc -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "type": "shell", 9 | "command": "dub test", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ali Akhtarzada 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Bolts Meta Programming Utility Library 2 | 3 | [![Latest version](https://img.shields.io/dub/v/bolts.svg)](https://code.dlang.org/packages/bolts) [![Build Status](https://travis-ci.org/aliak00/bolts.svg?branch=master)](https://travis-ci.org/aliak00/bolts) [![codecov](https://codecov.io/gh/aliak00/bolts/branch/master/graph/badge.svg)](https://codecov.io/gh/aliak00/bolts) [![license](https://img.shields.io/github/license/aliak00/bolts.svg)](https://github.com/aliak00/bolts/blob/master/LICENSE) 4 | 5 | Full API docs available [here](https://aliak00.github.io/bolts/bolts.html) 6 | 7 | Bolts is a utility library for the D programming language which contains a number of static reflection utilties that query compile time entities (traits) or transform them (meta). General utilties are in the modules `traits` and `meta`, and more specific ones are in dedicated modules (i.e. `bolts.members` provides utilities over a type's members). 8 | 9 | ## Modules: 10 | 11 | * **meta**: has functions that result in compile time entity transofrmations, including: 12 | * `Flatten`, `AliasPack`, `Pluck`, `Zip`, `FilterMembersOf`, `RemoveAttributes`. 13 | * **traits**: has general utitlites that can query compile time entities. including: 14 | * `isFunctionOver`, `isUnaryOver`, `isBinaryOver`, `isProperty`, `hasProperty`, `propertySemantics`, `areCombinable`, `isManifestAssignable`, `isOf`, `isSame`, `isNullType`, `StringOf`, `isRefType`, `isValueType`, `isLiteralOf`, `isLiteral`, `isCopyConstructable`, `isNonTriviallyCopyConstructable`, `protectionLevel`, `isTriviallyCopyConstructable`, `hasFunctionMember`, `areEquatable`, `isNullSettable`, `isNullTestable`, `isRefDecl`, `TypesOf` 15 | * **members**: has functions that allow you to query about the members of types 16 | * `staticMembersOf`, `memberFunctionsOf`, `member` (not eponymous) 17 | * **range**: query ranges 18 | * `isSortedRange`, `sortingPredicate`, `CommonTypeOfRanges` 19 | * **aa**: has functions that act on associative arrays 20 | * `isKey` (not eponymous) 21 | * **iz**: super non-eponymous template that provides a lot of the functionality that's in the traits module with a different sytax that allows their usage in meta functions as well. 22 | * **experimental**: contains experimental features 23 | *signatures: working implementation of type signatures 24 | *refraction: generate mixin strings to replicate a function, with some changes 25 | 26 | Most functions here operate on any compile time entity. For example `isUnaryOver` works in both these situatons: 27 | 28 | ```d 29 | int i; 30 | void f(int) {} 31 | isFunctionOver!(f, int); 32 | isFunctionOver!(f, 3); 33 | isFunctionOver!(f, i); 34 | ``` 35 | 36 | ## Iz super template 37 | 38 | The `iz` super template. Has a lot of the traits on types encapulated in one place. So if there's a trait that tells you something about a compile time entity, chances are `iz` will have it. E.g: 39 | 40 | ```d 41 | void f(int, float, string) {} 42 | iz!f.functionOver!(int, float, string); 43 | iz!f.functionOver!(3, float, ""); 44 | ``` 45 | 46 | ## Member super template 47 | 48 | The `member` super template, found in the `bolts.members` module is similar to the `iz` template but works on members of types only: 49 | 50 | ```d 51 | import bolts.members: member; 52 | struct S { 53 | static void f() {} 54 | } 55 | assert(member!(S, "f").exists); 56 | assert(member!(S, "f").protection == ProtectionLevel.public_); 57 | assert(!member!(S, "f").isProperty); 58 | ``` 59 | 60 | ## Signatures (experimental): 61 | 62 | Signatures are a way to enforce types to comply with other types. For example if you are making a range you can ensure your types conform to a range by mixing in a `Models` template to the type that needs it. You can also use the utilities provided here to constrain functions to types that adhere to a specific signature. 63 | 64 | ```d 65 | interface InputRange(T) { 66 | @property bool empty(); 67 | @property T front(); 68 | @ignoreAttributes void popFront(); 69 | } 70 | 71 | struct MyRange { 72 | mixin Models!(InputRange!int); 73 | } 74 | ``` 75 | 76 | The above will fail to compile with something like: 77 | 78 | ``` 79 | source/bolts/experimental/signatures.d(310,5): Error: static assert: "Type MyRange does not comply to signature InputRange!(int) 80 | Missing identifier empty of type bool. 81 | Missing identifier front of type int. 82 | Missing identifier popFront of function void(). 83 | source/bolts/experimental/signatures.d(464): <-- Signature InputRange!(int) defined here. 84 | source/bolts/experimental/signatures.d(471): <-- Checked here." 85 | ``` 86 | 87 | ## Refraction (experimental): 88 | 89 | It is sometimes necessary to create a function which is an exact copy of 90 | another function. Or sometimes it is necessary to introduce a few variations, 91 | while carrying all the other aspects. Because of function attributes, parameter 92 | storage classes and user-defined attributes, this requires building a string 93 | mixin. In addition, the mixed-in code must refer only to local names, if it is 94 | to work across module boundaires. This module facilitates the creation of such 95 | mixins. 96 | 97 | For example, this creates a function that has a different name and return type, 98 | but retains the 'pure' attribute from the original function: 99 | 100 | ```d 101 | pure int answer() { return 42; } 102 | mixin( 103 | refract!(answer, "answer").withName("realAnswer") 104 | .withReturnType("real") 105 | .mixture); 106 | static assert(is(typeof(realAnswer()) == real)); 107 | static assert(functionAttributes!realAnswer & FunctionAttribute.pure_); 108 | ``` 109 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bolts", 3 | "authors": [ 4 | "Ali Akhtarzada" 5 | ], 6 | "description": "Utility library for meta programming", 7 | "copyright": "Copyright © 2018, Ali Akhtarzada", 8 | "license": "MIT", 9 | "targetPath": "bin", 10 | "toolChainRequirements": { 11 | "frontend": ">=2.086" 12 | }, 13 | "configurations": [ 14 | { 15 | "dflags": [ 16 | "-mixin=mixins.txt" 17 | ], 18 | "name": "unittest", 19 | "targetType": "library" 20 | } 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /source/bolts/experimental/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Contains experimental features - these can break *anytime* 3 | */ 4 | module bolts.experimental; 5 | 6 | public { 7 | import bolts.experimental.signatures; 8 | } 9 | -------------------------------------------------------------------------------- /source/bolts/experimental/refraction.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module helps building functions from other functions. 3 | 4 | It is sometimes necessary to create a function which is an exact copy of 5 | another function. Or sometimes it is necessary to introduce a few variations, 6 | while carrying all the other aspects. Because of function attributes, 7 | parameter storage classes and user-defined attributes, this requires building 8 | a string mixin. In addition, the mixed-in code must refer only to local names, 9 | if it is to work across module boundaires. This problem and its solution are 10 | described by Adam D. Ruppe in a Tip of the Week, available here: 11 | https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854 12 | 13 | This module facilitates the creation of such mixins. 14 | 15 | +/ 16 | 17 | module bolts.experimental.refraction; 18 | 19 | import std.algorithm.iteration : map; 20 | import std.array; 21 | import std.format; 22 | import std.meta; 23 | import std.range : iota; 24 | import std.traits : functionAttributes, FunctionAttribute; 25 | 26 | // Do not require caller module to import 'std.traits'. Instead use our own 27 | // aliases in mixtures. 28 | alias ReturnType = std.traits.ReturnType; 29 | alias Parameters = std.traits.Parameters; 30 | 31 | /** 32 | Return a `Function` object that captures all the aspects of `fun`, using the 33 | value of `localName` to represent the return and parameter types, and the 34 | UDAs. Set the `Function`'s `overloadIndex` property to `index`, or to -1 if 35 | it is not specified. 36 | 37 | The `localName` parameter is, in general, *not* the function name. Rather, 38 | it is a compile-time expression that involves only symbols that exist in the 39 | caller's scope, for example a function alias passed as a template 40 | parameter. See 41 | https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854 42 | for a detailed explanation. 43 | 44 | Params: 45 | fun = a function 46 | localName = a string that represents `fun` in the caller's context 47 | overloadIndex = index of `fun` in its overload set, or -1 48 | */ 49 | 50 | Function refract(alias fun, string localName, int overloadIndex = -1)() 51 | if (is(typeof(fun) == function)) { 52 | Function model = { 53 | name: __traits(identifier, fun), 54 | overloadIndex: overloadIndex, 55 | localName: localName, 56 | returnType: __MODULE__~".ReturnType!("~localName~")", 57 | parameters: refractParameterList!(fun, localName), 58 | compactParameters: ( 59 | Parameters!fun.length > 0 60 | ? [ Parameter("_0", "%s.Parameters!(%s)".format(__MODULE__, localName)) ] 61 | : null), 62 | udas: __traits(getAttributes, fun) 63 | .length.iota.map!( 64 | formatIndex!( 65 | "@(__traits(getAttributes, %s)[%%d])".format(localName))).array, 66 | attributes: functionAttributes!(fun), 67 | static_: (__traits(isStaticFunction, fun) 68 | && isAggregate!(__traits(parent, fun))), 69 | body_: ";", 70 | }; 71 | 72 | return model; 73 | } 74 | 75 | /// 76 | unittest { 77 | pure @nogc int answer(lazy string question); 78 | alias F = answer; // typically F is a template argument 79 | static assert( 80 | refract!(F, "F").mixture == 81 | "pure @nogc @system %s.ReturnType!(F) answer(%s.Parameters!(F) _0);" 82 | .format(__MODULE__, __MODULE__)); 83 | } 84 | 85 | /// 86 | unittest { 87 | import std.format; 88 | import std.traits : FunctionAttribute; 89 | import bolts.experimental.refraction; 90 | 91 | interface GrandTour { 92 | pure int foo() immutable; 93 | @nogc @trusted nothrow ref int foo( 94 | out real, return ref int, lazy int) const; 95 | @safe shared scope void bar(scope Object); 96 | } 97 | 98 | class Mock(Interface) : Interface { 99 | static foreach (member; __traits(allMembers, Interface)) { 100 | static foreach (fun; __traits(getOverloads, Interface, member)) { 101 | mixin({ 102 | enum Model = refract!(fun, "fun"); 103 | if (is(ReturnType!fun == void)) { 104 | return Model.withBody("{}").mixture; 105 | } else if (Model.attributes & FunctionAttribute.ref_) { 106 | return Model.withBody(q{{ 107 | static %s rv; 108 | return rv; 109 | }}.format(Model.returnType)).mixture; 110 | } else { 111 | return Model.withBody(q{{ 112 | return %s.init; 113 | }}.format(Model.returnType)).mixture; 114 | } 115 | }()); 116 | } 117 | } 118 | } 119 | 120 | GrandTour mock = new Mock!GrandTour; 121 | real x; 122 | int i, l; 123 | mock.foo(x, i, l++) = 1; 124 | assert(mock.foo(x, i, l++) == 1); 125 | assert(l == 0); 126 | } 127 | 128 | private enum True(T...) = true; 129 | 130 | /** 131 | Return an array of `Function` objects, refracting the functions in `Scope` 132 | for which `IncludePredicate` evaluates to `true`, using the value of 133 | `localName` to represent `Scope`. `IncludePredicate` is optional; if not 134 | specified, refract all the functions. The `overloadIndex` property of each 135 | `Function` is set to the index of the function in its *entire* overload set 136 | (i.e. including the overloads that may have been excluded by 137 | `IncludePredicate`). 138 | 139 | Applying this function to a module, without specifying `IncludePredicate`, 140 | may severely affect compilation time, as *all* the properties of *all* 141 | functions in the module will be queried. 142 | 143 | Params: 144 | Scope = an aggregate or a module 145 | localName = a string mixin that represents `Scope` 146 | IncludePredicate = a template that takes an alias to a function and 147 | evaluates to a compile time boolean 148 | */ 149 | 150 | auto functionsOf( 151 | alias Scope, string localName, alias IncludePredicate = True)() 152 | if (is(Scope == module) || is(Scope == struct) 153 | || is(Scope == class) || is(Scope == interface) || is(Scope == union)) { 154 | Function[] functions; 155 | 156 | static foreach (member; __traits(allMembers, Scope)) { 157 | static foreach ( 158 | overloadIndex, fun; __traits(getOverloads, Scope, member)) { 159 | static if (IncludePredicate!fun) { 160 | functions ~= refract!( 161 | __traits(getOverloads, Scope, member)[overloadIndex], 162 | `__traits(getOverloads, %s, "%s")[%d]`.format( 163 | localName, member, overloadIndex), 164 | overloadIndex); 165 | } 166 | } 167 | } 168 | 169 | return functions; 170 | } 171 | 172 | /// 173 | unittest { 174 | static union Answers { 175 | int answer(); 176 | void answer(); 177 | string answer(); 178 | } 179 | 180 | alias Container = Answers; 181 | 182 | enum NotVoid(alias F) = !is(ReturnType!(F) == void); 183 | 184 | enum functions = functionsOf!(Container, "Container", NotVoid); 185 | 186 | static assert(functions.length == 2); 187 | 188 | static assert( 189 | functions[0].mixture == 190 | q{@system %s.ReturnType!(__traits(getOverloads, Container, "answer")[0]) answer();} 191 | .format(__MODULE__)); 192 | static assert(functions[0].overloadIndex == 0); 193 | 194 | static assert( 195 | functions[1].mixture == 196 | q{@system %s.ReturnType!(__traits(getOverloads, Container, "answer")[2]) answer();} 197 | .format(__MODULE__)); 198 | static assert(functions[1].overloadIndex == 2); 199 | } 200 | 201 | private enum isAggregate(T...) = 202 | is(T[0] == struct) || is(T[0] == union) || is(T[0] == class) 203 | || is(T[0] == interface); 204 | 205 | private mixin template replaceAttribute(NameValues...) { 206 | alias Struct = typeof(this); 207 | mixin( 208 | "Struct copy = {", 209 | { 210 | string[] mixture; 211 | foreach (member; __traits(allMembers, Struct)) { 212 | if (__traits(getOverloads, Struct, member).length == 0) { 213 | string valueMixture = member; 214 | static foreach (NameValue; NameValues) { 215 | if (member == NameValue.split(",")[0]) { 216 | valueMixture = NameValue.split(",")[1]; 217 | } 218 | } 219 | mixture ~= member ~ ":" ~ valueMixture; 220 | } 221 | } 222 | return mixture.join(",\n"); 223 | }(), 224 | "};" 225 | ); 226 | } 227 | 228 | unittest { 229 | static struct QA { 230 | string question; 231 | int answer; 232 | QA withQuestion(string value) { 233 | mixin replaceAttribute!"question,value"; 234 | return copy; 235 | } 236 | QA withQuestionAnswer(string q, int a) { 237 | mixin replaceAttribute!("question,q", "answer,a"); 238 | return copy; 239 | } 240 | } 241 | 242 | QA a42 = { answer: 42 }; 243 | enum question = "How many roads must a man walk down?"; 244 | assert(a42.withQuestion(question).question == question); 245 | assert(a42.withQuestion(question).answer == 42); 246 | 247 | QA def = { }; 248 | assert(def.withQuestionAnswer(question, 42).question == question); 249 | assert(def.withQuestionAnswer(question, 42).answer == 42); 250 | } 251 | 252 | /** 253 | A struct capturing all the aspects of a function necessary to produce a 254 | string mixin that re-creates the function (excepting the body). 255 | */ 256 | 257 | immutable struct Function { 258 | 259 | /** 260 | A string that evaluates to a symbol representing the function in the 261 | local context. 262 | */ 263 | 264 | string localName; 265 | 266 | /** 267 | Function name. Initial value: `__traits(identifier, fun)`. 268 | */ 269 | 270 | string name; 271 | 272 | /** 273 | Return a new `Function` object with the `name` attribute set to `value`. 274 | */ 275 | 276 | Function withName(string value) { 277 | mixin replaceAttribute!"name,value"; 278 | return copy; 279 | } 280 | 281 | /// 282 | unittest { 283 | pure @nogc int answer(); 284 | mixin(refract!(answer, "answer").withName("ultimateAnswer").mixture); 285 | static assert( 286 | __traits(getAttributes, ultimateAnswer) == 287 | __traits(getAttributes, answer)); 288 | } 289 | 290 | /** 291 | Index of function in its overload set, if created by `functionsOf`, or 292 | -1. 293 | */ 294 | 295 | int overloadIndex; 296 | 297 | /** 298 | Return type. Initial value: 299 | `bolts.experimental.refraction.ReturnType!fun`. 300 | */ 301 | 302 | string returnType; 303 | 304 | /** 305 | Return a new `Function` object with the `returnType` attribute set to 306 | `value`. 307 | */ 308 | 309 | Function withReturnType(string value) { 310 | mixin replaceAttribute!"returnType,value"; 311 | return copy; 312 | } 313 | 314 | /// 315 | unittest { 316 | pure int answer() { return 42; } 317 | mixin( 318 | refract!(answer, "answer") 319 | .withName("realAnswer") 320 | .withReturnType("real") 321 | .mixture); 322 | static assert(is(typeof(realAnswer()) == real)); 323 | static assert(functionAttributes!realAnswer & FunctionAttribute.pure_); 324 | } 325 | 326 | /** 327 | Function parameters. Initial value: from the refracted function. 328 | */ 329 | 330 | Parameter[] parameters; 331 | 332 | // Starts off as Parameters!fun, set to null if parameter list is edited. 333 | private Parameter[] compactParameters; 334 | 335 | private auto bestParameters() { 336 | return compactParameters != null ? compactParameters : parameters; 337 | } 338 | 339 | /** 340 | Return a new `Function` object with the parameters attribute set to 341 | `value`. 342 | */ 343 | 344 | Function withParameters(immutable(Parameter)[] value) { 345 | mixin replaceAttribute!("parameters,value", "compactParameters,null"); 346 | return copy; 347 | } 348 | 349 | /// 350 | unittest { 351 | int answer(); 352 | mixin( 353 | refract!(answer, "answer") 354 | .withName("answerQuestion") 355 | .withParameters( 356 | [ Parameter().withName("question").withType("string")]) 357 | .mixture); 358 | int control(string); 359 | static assert(is(Parameters!answerQuestion == Parameters!control)); 360 | } 361 | 362 | /** 363 | Return a new `Function` object with `newParameters` inserted at the 364 | specified `index` in the `attributes`. 365 | */ 366 | 367 | Function withParametersAt( 368 | uint index, immutable(Parameter)[] newParameters...) { 369 | auto value = index == parameters.length ? parameters ~ newParameters 370 | : index == 0 ? newParameters ~ parameters 371 | : parameters[0..index] ~ newParameters ~ parameters[index..$]; 372 | mixin replaceAttribute!("parameters,value", "compactParameters,null"); 373 | return copy; 374 | } 375 | 376 | /** 377 | Function body. Initial value: `;`. 378 | */ 379 | 380 | string body_; 381 | 382 | /** 383 | Return a new `Function` object with the `body_` attribute set to 384 | `value`. 385 | */ 386 | 387 | Function withBody(string value) { 388 | mixin replaceAttribute!"body_,value"; 389 | return copy; 390 | } 391 | 392 | /// 393 | unittest { 394 | pure int answer(); 395 | mixin( 396 | refract!(answer, "answer").withName("theAnswer") 397 | .withBody("{ return 42; }") 398 | .mixture); 399 | static assert(theAnswer() == 42); 400 | } 401 | 402 | /** 403 | Function attributes. 404 | Initial value: `std.traits.functionAttributes!fun` 405 | */ 406 | 407 | ulong attributes; 408 | 409 | /** 410 | Return a new `Function` object with the `attributes` attribute set to 411 | `value`. 412 | */ 413 | 414 | Function withAttributes(uint value) { 415 | mixin replaceAttribute!"attributes,value"; 416 | return copy; 417 | } 418 | 419 | /// 420 | unittest { 421 | nothrow int answer(); 422 | enum model = refract!(answer, "answer"); 423 | with (FunctionAttribute) { 424 | mixin( 425 | model 426 | .withName("pureAnswer") 427 | .withAttributes(model.attributes | pure_) 428 | .mixture); 429 | static assert(functionAttributes!pureAnswer & pure_); 430 | static assert(functionAttributes!pureAnswer & nothrow_); 431 | } 432 | } 433 | 434 | /** 435 | If `true`, prefix generated function with `static`. Initial value: 436 | `true` if the refracted function is a static *member* function inside a 437 | struct, class, interface, or union. 438 | */ 439 | 440 | bool static_; 441 | 442 | /** 443 | Return a new `Function` object with the `static_` attribute set to 444 | `value`. 445 | */ 446 | 447 | Function withStatic(bool value) { 448 | mixin replaceAttribute!"static_,value"; 449 | return copy; 450 | } 451 | 452 | /// 453 | unittest { 454 | struct Question { 455 | static int answer() { return 42; } 456 | } 457 | mixin( 458 | refract!(Question.answer, "Question.answer") 459 | .withStatic(false) 460 | .withBody("{ return Question.answer; }") 461 | .mixture); 462 | static assert(answer() == 42); 463 | } 464 | 465 | /** 466 | User defined attributes. 467 | Initial value: 468 | `bolts.experimental.refraction.ParameterAttributes!(fun, parameterIndex)...[attributeIndex...])`. 469 | */ 470 | 471 | string[] udas; 472 | 473 | /** 474 | Return a new `Function` object with the `udas` attribute set to `value`. 475 | */ 476 | 477 | Function withUdas(immutable(string)[] value) { 478 | mixin replaceAttribute!"udas,value"; 479 | return copy; 480 | } 481 | 482 | /// 483 | unittest { 484 | import std.typecons : tuple; 485 | @(666) int answer(); 486 | 487 | mixin( 488 | refract!(answer, "answer") 489 | .withName("answerIs42") 490 | .withUdas(["@(42)"]) 491 | .mixture); 492 | static assert(__traits(getAttributes, answerIs42).length == 1); 493 | static assert(__traits(getAttributes, answerIs42)[0] == 42); 494 | } 495 | 496 | /** 497 | Return mixin code for this `Function`. 498 | */ 499 | 500 | string mixture() { 501 | return join( 502 | udas ~ 503 | attributeMixtureArray() ~ 504 | [ 505 | returnType, 506 | name ~ "(" ~ parameterListMixture ~ ")", 507 | ], " ") ~ 508 | body_; 509 | } 510 | 511 | /** 512 | Return the parameter list as an array of strings. 513 | */ 514 | 515 | string[] parameterListMixtureArray() { 516 | return map!(p => p.mixture)(bestParameters()).array; 517 | } 518 | 519 | /** 520 | Return the parameter list as a strings. 521 | */ 522 | 523 | string parameterListMixture() { 524 | return parameterListMixtureArray.join(", "); 525 | } 526 | 527 | /** 528 | Return the argument list as an array of strings. 529 | */ 530 | 531 | const(string)[] argumentMixtureArray() { 532 | return bestParameters.map!(p => p.name).array; 533 | } 534 | 535 | /// 536 | unittest { 537 | int add(int a, int b); 538 | static assert( 539 | refract!(add, "add").argumentMixtureArray == [ "_0" ]); 540 | } 541 | 542 | /** 543 | Return the argument list as a string. 544 | */ 545 | 546 | string argumentMixture() { 547 | return argumentMixtureArray.join(", "); 548 | } 549 | 550 | /// 551 | unittest { 552 | int add(int a, int b); 553 | static assert(refract!(add, "add").argumentMixture == "_0"); 554 | } 555 | 556 | /** 557 | Return the attribute list as an array of strings. 558 | */ 559 | 560 | string[] attributeMixtureArray() { 561 | with (FunctionAttribute) { 562 | return [] 563 | ~ (static_ ? ["static"] : []) 564 | ~ (attributes & pure_ ? ["pure"] : []) 565 | ~ (attributes & nothrow_ ? ["nothrow"] : []) 566 | ~ (attributes & property ? ["@property"] : []) 567 | ~ (attributes & trusted ? ["@trusted"] : []) 568 | ~ (attributes & safe ? ["@safe"] : []) 569 | ~ (attributes & nogc ? ["@nogc"] : []) 570 | ~ (attributes & system ? ["@system"] : []) 571 | ~ (attributes & const_ ? ["const"] : []) 572 | ~ (attributes & immutable_ ? ["immutable"] : []) 573 | ~ (attributes & inout_ ? ["inout"] : []) 574 | ~ (attributes & shared_ ? ["shared"] : []) 575 | ~ (attributes & return_ ? ["return"] : []) 576 | ~ (attributes & scope_ ? ["scope"] : []) 577 | ~ (attributes & ref_ ? ["ref"] : []) 578 | ; 579 | } 580 | } 581 | 582 | /// 583 | unittest { 584 | nothrow pure int answer(); 585 | enum model = refract!(answer, "answer"); 586 | static assert( 587 | model.attributeMixtureArray == ["pure", "nothrow", "@system"]); 588 | } 589 | 590 | /** 591 | Return the attribute list as a string. 592 | */ 593 | 594 | string attributeMixture() { 595 | return attributeMixtureArray.join(" "); 596 | } 597 | 598 | /// 599 | unittest { 600 | nothrow pure int answer(); 601 | enum model = refract!(answer, "answer"); 602 | static assert(model.attributeMixture == "pure nothrow @system"); 603 | } 604 | } 605 | 606 | /** 607 | A struct capturing all the properties of a function parameter. 608 | */ 609 | 610 | immutable struct Parameter { 611 | /** 612 | Parameter name. Initial value: `_i`, where `i` is the position of the 613 | parameter. 614 | */ 615 | 616 | string name; 617 | 618 | /** 619 | Return a new Parameter object with the `name` attribute set to `value`. 620 | */ 621 | 622 | Parameter withName(string value) { 623 | mixin replaceAttribute!("name,value"); 624 | return copy; 625 | } 626 | 627 | /** 628 | Parameter type. Initial value: `std.traits.Parameter!fun[i]`, where 629 | `fun` is the refracted function and `i` is the position of the 630 | parameter. 631 | */ 632 | 633 | string type; 634 | 635 | /** 636 | Return a new `Parameter` object with the `type` attribute set to 637 | `value`. 638 | */ 639 | 640 | Parameter withType(string value) { 641 | mixin replaceAttribute!("type,value", "compactMixture,null"); 642 | return copy; 643 | } 644 | 645 | /** 646 | Parameter storage classes. Initial value: 647 | `[__traits(getParameterStorageClasses, fun, i)]`, where where `fun` is 648 | the refracted function and `i` is the position of the parameter. 649 | */ 650 | 651 | string[] storageClasses; 652 | 653 | /** 654 | Return a new `Parameter` object with the `storageClasses` attribute set 655 | to `value`. 656 | */ 657 | 658 | Parameter withStorageClasses(immutable(string)[] value) { 659 | mixin replaceAttribute!("storageClasses,value", "compactMixture,null"); 660 | return copy; 661 | } 662 | 663 | /** 664 | Parameter UDAs. Initial value: 665 | `[@(bolts.experimental.refraction.ParameterAttribute!(fun,i)[j...])]`, 666 | where where `fun` is the refracted function, `i` is the position of the 667 | parameter, and `j...` are the positions of the UDAs. 668 | */ 669 | 670 | string[] udas; 671 | 672 | /** 673 | Return a new `Parameter` object with the `udas` attribute set to 674 | `value`. 675 | */ 676 | 677 | Parameter withUdas(immutable(string)[] value) { 678 | mixin replaceAttribute!("udas,value", "compactMixture,null"); 679 | return copy; 680 | } 681 | 682 | // Parameters!fun[i..i+1]. 683 | private string[] compactMixture; 684 | 685 | string mixture() { 686 | auto typeMixture = compactMixture != null 687 | ? compactMixture 688 | : udas ~ storageClasses ~ [ type ]; 689 | return join( 690 | name.length > 0 ? typeMixture ~ [ name ] : typeMixture, 691 | " "); 692 | } 693 | } 694 | 695 | private Parameter refractParameter(alias Fun, string mixture, uint index)() { 696 | static if (is(typeof(Fun) parameters == __parameters)) { 697 | alias parameter = parameters[index .. index + 1]; 698 | static if (__traits(compiles, __traits(getAttributes, parameter))) { 699 | enum udaFormat = "@(%s.ParameterAttributes!(%s, %d)[%%d])".format( 700 | __MODULE__, mixture, index); 701 | enum udas = __traits(getAttributes, parameter).length.iota.map!( 702 | formatIndex!udaFormat).array; 703 | } else { 704 | enum udas = []; 705 | } 706 | 707 | Parameter p = { 708 | compactMixture: [ 709 | "%s.Parameters!(%s)[%d..%d]".format(__MODULE__, mixture, index, index + 1) 710 | ], 711 | type: `%s.Parameters!(%s)[%d]`.format(__MODULE__, mixture, index), 712 | name: "_%d".format(index), 713 | storageClasses: [__traits(getParameterStorageClasses, Fun, index)], 714 | udas: udas, 715 | }; 716 | } 717 | return p; 718 | } 719 | 720 | private Parameter[] refractParameterList(alias Fun, string mixture)() { 721 | Parameter[] result; 722 | static if (is(typeof(Fun) parameters == __parameters)) { 723 | static foreach (i; 0 .. parameters.length) { 724 | result ~= refractParameter!(Fun, mixture, i); 725 | } 726 | } 727 | return result; 728 | } 729 | 730 | private string formatIndex(string f)(ulong i) { 731 | return format!f(i); 732 | } 733 | 734 | /** 735 | Return an alias to the `j`-th user-define attribute of the `i`-th parameter 736 | of `fun`. 737 | 738 | Params: 739 | fun = a function 740 | i = zero-based index of a parameter of fun 741 | j = zero-based index of a user-defined attribute of i-th parameter fun 742 | */ 743 | 744 | template ParameterAttributes(alias fun, int i) { 745 | static if (is(typeof(fun) P == __parameters)) { 746 | alias ParameterAttributes = 747 | __traits(getAttributes, P[i..i+1]); 748 | } 749 | } 750 | 751 | unittest { 752 | struct virtual; 753 | void kick(int times, @virtual @("Animal") Object animal); 754 | 755 | static assert(ParameterAttributes!(kick, 1).length == 2); 756 | static assert(is(ParameterAttributes!(kick, 1)[0] == virtual)); 757 | static assert(ParameterAttributes!(kick, 1)[1] == "Animal"); 758 | 759 | import bolts.experimental.refraction; 760 | enum kickModel = refract!(kick, "kick"); 761 | 762 | mixin(kickModel.withName("pet").mixture); 763 | static assert(is(typeof(pet) == typeof(kick))); 764 | 765 | mixin( 766 | kickModel 767 | .withName("feed") 768 | .withParameters( 769 | [ kickModel.parameters[0].withUdas(kickModel.parameters[1].udas), 770 | kickModel.parameters[1].withUdas(kickModel.parameters[0].udas) ]) 771 | .mixture); 772 | 773 | static assert( 774 | ParameterAttributes!(feed, 0).stringof == 775 | ParameterAttributes!(kick, 1).stringof); 776 | static assert( 777 | ParameterAttributes!(feed, 1).stringof == 778 | ParameterAttributes!(kick, 0).stringof); 779 | } 780 | 781 | unittest { 782 | int answer(); 783 | enum answerModel = refract!(answer, "answer"); 784 | static assert(answerModel.parameterListMixture == ""); 785 | 786 | mixin(answerModel.withName("copy").mixture); 787 | static assert(is(typeof(answer) == typeof(copy))); 788 | } 789 | 790 | unittest { 791 | // Test compact parameters. 792 | @system int answer(lazy string question); 793 | enum answerModel = refract!(answer, "answer"); 794 | 795 | // Parameters not modified: use compact mixture. 796 | static assert( 797 | answerModel.parameterListMixture == 798 | "%s.Parameters!(answer) _0".format(__MODULE__)); 799 | 800 | mixin(answerModel.withName("copy").mixture); 801 | static assert(is(typeof(answer) == typeof(copy))); 802 | 803 | // Edit parameter list: use compact mixture for original parameter. 804 | static assert( 805 | answerModel 806 | .withParametersAt(0, Parameter().withType("int")) 807 | .parameterListMixture == 808 | "int, %s.Parameters!(answer)[0..1] _0".format(__MODULE__)); 809 | 810 | // Edit storage class: just use the type from the original parameter. 811 | static assert( 812 | answerModel.parameters[0] 813 | .withStorageClasses([ "ref" ]) 814 | .mixture == 815 | "ref %s.Parameters!(answer)[0] _0".format(__MODULE__)); 816 | } 817 | -------------------------------------------------------------------------------- /source/bolts/experimental/signatures.d: -------------------------------------------------------------------------------- 1 | /** 2 | Provides utilites that allow you to enforce signatures - a specification for a structure 3 | */ 4 | module bolts.experimental.signatures; 5 | 6 | /// 7 | unittest { 8 | interface InputRange(T) { 9 | @property bool empty(); 10 | @property T front(); 11 | @ignoreAttributes void popFront(); 12 | } 13 | 14 | struct R(T) { 15 | mixin Models!(InputRange!T); 16 | 17 | T[] values; 18 | int index; 19 | this(T[] arr) { 20 | values = arr; 21 | } 22 | @property bool empty() { 23 | return values.length == index; 24 | } 25 | @property T front() { 26 | return values[index]; 27 | } 28 | void popFront() { 29 | index++; 30 | } 31 | } 32 | 33 | import std.range: array; 34 | auto r = R!int([1, 4, 2, 3]); 35 | assert(r.array == [1, 4, 2, 3]); 36 | } 37 | 38 | private enum Report { 39 | all, 40 | one, 41 | } 42 | 43 | private auto checkSignatureOf(alias Model, alias Sig, Report report = Report.one, string path = "")() { 44 | import bolts.traits: StringOf; 45 | import bolts.meta: RemoveAttributes; 46 | import std.traits: hasMember, isAggregateType, isNested, OriginalType; 47 | import std.conv: to; 48 | 49 | alias sigMember(string member) = __traits(getMember, Sig, member); 50 | alias modelMember(string member) = __traits(getMember, Model, member); 51 | 52 | string typeToString(T)() { 53 | import std.traits: isFunction; 54 | static if (is(T == struct)) { 55 | return "struct"; 56 | } else static if (is(T == class)) { 57 | return "class"; 58 | } else static if (is(T == union)) { 59 | return "union"; 60 | } else static if (is(T == interface)) { 61 | return "interface"; 62 | } else static if (is(T == enum)) { 63 | return "enum"; 64 | } else static if (isFunction!T) { 65 | return "function type"; 66 | } else { 67 | return "type"; 68 | } 69 | } 70 | 71 | string errorPrefix(string prefix)() { 72 | string lower() { 73 | if (prefix[0] >= 'A' && prefix[0] <= 'Z') { 74 | return prefix[0] + 32 ~ prefix[1 .. $]; 75 | } else { 76 | return prefix; 77 | } 78 | } 79 | 80 | string upper() { 81 | if (prefix[0] >= 'a' && prefix[0] <= 'z') { 82 | return prefix[0] - 32 ~ prefix[1 .. $]; 83 | } else { 84 | return prefix; 85 | } 86 | } 87 | 88 | if (path.length) { 89 | return "Type `" ~ path ~ "` " ~ lower(); 90 | } else { 91 | return upper(); 92 | } 93 | } 94 | 95 | string checkTypedIdentifier(string member, SigMember)() { 96 | 97 | import std.traits: isFunction, hasUDA, hasStaticMember; 98 | 99 | enum error = errorPrefix!"Missing identifier `" 100 | ~ member 101 | ~ "` of " 102 | ~ typeToString!SigMember 103 | ~ " `" 104 | ~ (hasStaticMember!(Sig, member) ? "static " : "") 105 | ~ StringOf!SigMember 106 | ~ "`."; 107 | 108 | bool staticCheckPass() { 109 | return !hasStaticMember!(Sig, member) 110 | || hasStaticMember!(Model, member); 111 | } 112 | 113 | static if (is(typeof(modelMember!member) ModelMember)) { 114 | 115 | if (!staticCheckPass()) { 116 | return error; 117 | } 118 | 119 | static if (hasUDA!(sigMember!member, ignoreAttributes)) { 120 | alias T = RemoveAttributes!SigMember; 121 | alias U = RemoveAttributes!ModelMember; 122 | } else { 123 | static if (hasUDA!(sigMember!member, ignoreQualifiers)) { 124 | import std.traits: Unqual; 125 | alias T = Unqual!SigMember; 126 | alias U = Unqual!ModelMember; 127 | } else { 128 | alias T = SigMember; 129 | alias U = ModelMember; 130 | } 131 | } 132 | 133 | static if (is(T == U)) { 134 | return null; 135 | } else { 136 | return error; 137 | } 138 | 139 | } else { 140 | return error; 141 | } 142 | } 143 | 144 | string checkEnum(string member, ModelMember, SigMember)() { 145 | static if (is(ModelMember == enum) && is(OriginalType!SigMember == OriginalType!ModelMember)) { 146 | import std.algorithm: sort, setDifference; 147 | auto sigMembers = [__traits(allMembers, SigMember)].sort; 148 | auto modelMembers = [__traits(allMembers, ModelMember)].sort; 149 | if (sigMembers != modelMembers) { 150 | return errorPrefix!"Enum `" 151 | ~ member 152 | ~ "` is missing members: " 153 | ~ sigMembers.setDifference(modelMembers).to!string; 154 | } 155 | return null; 156 | } else { 157 | return errorPrefix!"Missing enum named `" 158 | ~ member 159 | ~ "` of type `" 160 | ~ StringOf!SigMember 161 | ~ " with original type `" 162 | ~ StringOf!(OriginalType!SigMember) 163 | ~ "`."; 164 | } 165 | } 166 | 167 | string checkAlias(string member, ModelMember, SigMember)() { 168 | static if (!is(SigMember == ModelMember)) { 169 | return errorPrefix!"Found alias `" 170 | ~ member 171 | ~ "` of wrong type. Expected alias to " 172 | ~ typeToString!SigMember 173 | ~ " `" 174 | ~ StringOf!SigMember 175 | ~ "`."; 176 | } else { 177 | return null; 178 | } 179 | } 180 | 181 | auto checkType(string member, SigMember)() { 182 | static if (is(modelMember!member ModelMember)) { 183 | static if (member != StringOf!SigMember) { 184 | if (auto error = checkAlias!(member, ModelMember, SigMember)) { 185 | return error; 186 | } 187 | } else static if (is(SigMember == enum)) { 188 | if (auto error = checkEnum!(member, ModelMember, SigMember)) { 189 | return error; 190 | } 191 | } else static if (isAggregateType!SigMember) { 192 | if (auto error = checkSignatureOf!(ModelMember, SigMember, report, path ~ "." ~ StringOf!ModelMember)) { 193 | return error; 194 | } 195 | } 196 | return null; 197 | } else { 198 | static if (StringOf!SigMember != member) { 199 | return errorPrefix!"Missing alias named `" 200 | ~ member 201 | ~ "` to " 202 | ~ typeToString!SigMember 203 | ~ " `" 204 | ~ StringOf!SigMember 205 | ~ "`."; 206 | } else { 207 | return errorPrefix!"Missing " 208 | ~ typeToString!SigMember 209 | ~ " named `" 210 | ~ member 211 | ~ "`"; 212 | } 213 | } 214 | } 215 | 216 | string checkUnknown(string member)() { 217 | static if (isNested!Sig && member == "this") { 218 | return null; 219 | } else { 220 | return "Don`t know member `" ~ member ~ "` of type `" ~ StringOf!Model ~ "`"; 221 | } 222 | } 223 | 224 | static if (report == Report.all) { 225 | string[] result; 226 | } else { 227 | string result; 228 | } 229 | 230 | immutable storeResult = q{ 231 | static if (report == Report.one) { 232 | result = error; 233 | break; 234 | } else { 235 | result ~= error; 236 | } 237 | }; 238 | 239 | foreach (member; __traits(allMembers, Sig)) { 240 | static if (is(typeof(sigMember!member) T)) { 241 | if (auto error = checkTypedIdentifier!(member, T)) { 242 | mixin(storeResult); 243 | } 244 | } else static if (is(sigMember!member T)) { 245 | if (auto error = checkType!(member, T)) { 246 | mixin(storeResult); 247 | } 248 | } else { 249 | if (auto error = checkUnknown!member) { 250 | mixin(storeResult); 251 | } 252 | } 253 | } 254 | return result; 255 | } 256 | 257 | unittest { 258 | struct X { 259 | alias b = int; alias c = float; enum E1 { one } void f(int) {} 260 | enum E2 { a, b } int x; float y; short z; enum E3 { a, b } 261 | struct Inner { 262 | struct AnotherInner { 263 | int a; 264 | int b; 265 | } 266 | } 267 | struct MissingInner { 268 | struct A {} 269 | } 270 | static int s; 271 | } 272 | struct Y { 273 | alias a = int; alias c = int; 274 | enum E2 { a } int x; int z; enum E3 { a, b } 275 | struct Inner { 276 | struct AnotherInner { 277 | int a; 278 | int b; 279 | } 280 | } 281 | struct MissingInner {} 282 | } 283 | 284 | const expectedErrors = [ 285 | "Missing alias named `b` to type `int`.", 286 | "Found alias `c` of wrong type. Expected alias to type `float`.", 287 | "Missing enum named `E1`", 288 | "Missing identifier `f` of function type `void(int)`.", 289 | "Enum `E2` is missing members: [\"b\"]", 290 | "Missing identifier `y` of type `float`.", 291 | "Missing identifier `z` of type `short`.", 292 | "Type `.MissingInner` missing struct named `A`", 293 | "Missing identifier `s` of type `static int`.", 294 | ]; 295 | 296 | assert(checkSignatureOf!(Y, X, Report.all) == expectedErrors); 297 | } 298 | 299 | /** 300 | Checks if type `Model` is a model of type `Sig` 301 | */ 302 | template isModelOf(alias _Model, alias _Sig) { 303 | import bolts.traits: TypesOf; 304 | alias Model = TypesOf!_Model[0]; 305 | alias Sig = TypesOf!_Sig[0]; 306 | enum isModelOf = checkSignatureOf!(Model, Sig, Report.one) == null; 307 | } 308 | 309 | /** 310 | Asserts that the given model follows the specification of the given signature 311 | */ 312 | template AssertModelOf(alias _Model, alias _Sig, string file = __FILE__, int line = __LINE__) { 313 | import std.algorithm: map, joiner; 314 | import std.range: array; 315 | import std.conv: to; 316 | import bolts.traits: StringOf, TypesOf; 317 | 318 | alias Model = TypesOf!_Model[0]; 319 | alias Sig = TypesOf!_Sig[0]; 320 | 321 | string addLocation(string str) { 322 | template symLoc(alias sym) { 323 | template format(string file, int line, int _) { 324 | enum format = file ~ "(" ~ to!string(line) ~ ")"; 325 | } 326 | enum symLoc = format!(__traits(getLocation, sym)); 327 | } 328 | enum assertLoc = file ~ "(" ~ to!string(line) ~ ")"; 329 | return str 330 | ~ "\n " 331 | ~ symLoc!Sig 332 | ~ ": <-- Signature `" 333 | ~ StringOf!Sig 334 | ~ "` defined here.\n " 335 | ~ assertLoc 336 | ~ ": <-- Checked here."; 337 | } 338 | 339 | immutable errors = checkSignatureOf!(Model, Sig, Report.all); 340 | 341 | static assert( 342 | errors.length == 0, 343 | "Type `" ~ StringOf!Model ~ "` does not comply to signature `" ~ StringOf!Sig ~ "`" 344 | ~ errors 345 | .map!(s => "\n " ~ s) 346 | .joiner 347 | .to!string 348 | .addLocation 349 | ); 350 | 351 | enum AssertModelOf = true; 352 | } 353 | 354 | /// 355 | unittest { 356 | struct X { int a; float z; } 357 | struct Y { int a; float z; } 358 | struct Z { int b; float z; } 359 | 360 | static assert(isModelOf!(Y, X)); 361 | } 362 | 363 | /** 364 | Mixin that ensures a type models the desired signature of a structure 365 | */ 366 | mixin template Models(alias Sig, string file = __FILE__, int line = __LINE__) { 367 | static import bolts.experimental; 368 | static assert(bolts.experimental.signatures.AssertModelOf!(typeof(this), Sig, file, line)); 369 | } 370 | 371 | /// 372 | unittest { 373 | struct Sig { 374 | alias I = int; 375 | int x; 376 | float y; 377 | struct Inner { int a; struct X { int b; } } 378 | int f(int) { return 0; } 379 | enum X { one, two } 380 | union L { int a; } 381 | } 382 | 383 | struct Y { 384 | mixin Models!Sig; 385 | alias I = int; 386 | int x; 387 | float y; 388 | struct Inner { int a; struct X { int b; } } 389 | int f(int) { return 0; } 390 | enum X { one, two } 391 | union L { int a; } 392 | } 393 | 394 | static assert(isModelOf!(Y, Sig)); 395 | } 396 | 397 | unittest { 398 | struct TemplatedSig(T) { 399 | T value; 400 | } 401 | 402 | struct Y(T) { 403 | mixin Models!(TemplatedSig!T); 404 | T value; 405 | } 406 | 407 | static assert(__traits(compiles, { 408 | Y!int x; 409 | })); 410 | } 411 | 412 | unittest { 413 | struct Sig { 414 | alias I = int; 415 | int x; 416 | float y; 417 | } 418 | 419 | static assert(!__traits(compiles, { 420 | struct X { 421 | mixin Models!Sig; 422 | alias I = float; 423 | int x; 424 | float y; 425 | } 426 | })); 427 | 428 | static assert(!__traits(compiles, { 429 | struct X { 430 | mixin Models!Sig; 431 | int I; 432 | int x; 433 | float y; 434 | } 435 | })); 436 | 437 | static assert(!__traits(compiles, { 438 | struct X { 439 | mixin Models!Sig; 440 | alias M = int; 441 | int x; 442 | float y; 443 | } 444 | })); 445 | } 446 | 447 | unittest { 448 | struct Sig { 449 | alias I = int; 450 | int x; 451 | float y; 452 | } 453 | 454 | static assert(!__traits(compiles, { 455 | struct X { 456 | mixin Models!Sig; 457 | alias I = float; 458 | float x; 459 | float y; 460 | } 461 | })); 462 | 463 | static assert(!__traits(compiles, { 464 | struct X { 465 | mixin Models!Sig; 466 | int I; 467 | alias x = int; 468 | float y; 469 | } 470 | })); 471 | 472 | static assert(!__traits(compiles, { 473 | struct X { 474 | mixin Models!Sig; 475 | alias M = int; 476 | int x; 477 | } 478 | })); 479 | } 480 | 481 | /** 482 | Attribute that can be applied on identifiers in a signature that will let the model checker know not to 483 | take attributes in to account 484 | */ 485 | struct ignoreAttributes {} 486 | 487 | /** 488 | Attribute that can be applied on identifiers in a signature that will let the model checker know not to 489 | take type qualifiers in to account 490 | */ 491 | struct ignoreQualifiers {} 492 | 493 | unittest { 494 | interface Sig { 495 | static @property string name(); 496 | @ignoreQualifiers static @property string help(); 497 | int run(string[]); 498 | } 499 | 500 | struct X { 501 | mixin Models!Sig; 502 | 503 | static string name = "hello"; 504 | immutable static string help = "help"; 505 | int run(string[] args) { 506 | return 0; 507 | } 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /source/bolts/from.d: -------------------------------------------------------------------------------- 1 | /** 2 | Lazy import symbols 3 | */ 4 | module bolts.from; 5 | 6 | /** 7 | Encompases the from import idiom in an opDispatch version 8 | 9 | Since: 10 | - 0.12.0 11 | 12 | See_Also: 13 |
  • https://dlang.org/blog/2017/02/13/a-new-import-idiom/ 14 |
  • https://forum.dlang.org/thread/gdipbdsoqdywuabnpzpe@forum.dlang.org 15 | */ 16 | enum from = FromImpl!null(); 17 | 18 | /// 19 | unittest { 20 | // Call a function 21 | auto _0 = from.std.algorithm.map!"a"([1, 2, 3]); 22 | // Assign an object 23 | auto _1 = from.std.datetime.stopwatch.AutoStart.yes; 24 | 25 | // compile-time constraints 26 | auto length(R)(R range) if (from.std.range.isInputRange!R) { 27 | return from.std.range.walkLength(range); 28 | } 29 | 30 | assert(length([1, 2]) == 2); 31 | } 32 | 33 | private template CanImport(string moduleName) { 34 | enum CanImport = __traits(compiles, { mixin("import ", moduleName, ";"); }); 35 | } 36 | 37 | private template ModuleContainsSymbol(string moduleName, string symbolName) { 38 | enum ModuleContainsSymbol = CanImport!moduleName && __traits(compiles, { 39 | mixin("import ", moduleName, ":", symbolName, ";"); 40 | }); 41 | } 42 | 43 | private struct FromImpl(string moduleName) { 44 | template opDispatch(string symbolName) { 45 | static if (ModuleContainsSymbol!(moduleName, symbolName)) { 46 | mixin("import ", moduleName,";"); 47 | mixin("alias opDispatch = ", symbolName, ";"); 48 | } else { 49 | static if (moduleName.length == 0) { 50 | enum opDispatch = FromImpl!(symbolName)(); 51 | } else { 52 | enum importString = moduleName ~ "." ~ symbolName; 53 | static assert( 54 | CanImport!importString, 55 | "Symbol \"" ~ symbolName ~ "\" not found in " ~ modueName 56 | ); 57 | enum opDispatch = FromImpl!importString(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | unittest { 64 | static assert(!__traits(compiles, { from.std.stdio.thisFunctionDoesNotExist(42); })); 65 | } 66 | -------------------------------------------------------------------------------- /source/bolts/internal.d: -------------------------------------------------------------------------------- 1 | module bolts.internal; 2 | 3 | public import bolts.from; 4 | 5 | package template ResolvePointer(T) { 6 | import std.traits: isPointer, PointerTarget; 7 | static if (isPointer!T) { 8 | alias ResolvePointer = PointerTarget!T; 9 | } else { 10 | alias ResolvePointer = T; 11 | } 12 | } 13 | 14 | version (unittest) { 15 | // Just here so can be used in unittests without importing all the time 16 | package import std.stdio: writeln; 17 | 18 | // Defines a set of types that can be used to test copy constructable related traits 19 | package mixin template copyConstructableKinds() { 20 | static struct KindPOD {} 21 | static struct KindHasCopyContrustor { this(ref inout typeof(this)) inout {} } 22 | static struct KindHasPostBlit { this(this) {} } 23 | static struct KindContainsPOD { KindPOD value; } 24 | static struct KindContainsTypeWithNonTrivialCopyConstructor { KindHasCopyContrustor value; } 25 | static struct KindContainsTypeWithPostBlit { KindHasPostBlit value; } 26 | 27 | static assert(__traits(isPOD, KindPOD)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/bolts/iz.d: -------------------------------------------------------------------------------- 1 | /** 2 | Iz is is - reason for choosing iz is because is is a keyword. Answers the qustions "is this 'thing' ____ ?" 3 | */ 4 | module bolts.iz; 5 | 6 | import bolts.internal; 7 | 8 | /** 9 | `Iz` is a helper template that allows you to inspect a type or an alias. 10 | 11 | It takes a single element as an argument. The reason the template parameters is 12 | types as a variable arg sequence is becure if D does not allow (pre version 2.087) 13 | to say "I want either a type or alias over here, I don't care, I'll figure it out". 14 | But it does allow it when you use a $(I template sequence parameter) 15 | 16 | $(TABLE 17 | $(TR $(TH method) $(TH Description)) 18 | $(TR 19 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.of, `of`)) 20 | $(TD True if the resolved type is the same as another resolved type) 21 | ) 22 | $(TR 23 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.sameAs, `sameAs`)) 24 | $(TD True if T and U are the same "thing" (type, alias, literal value)) 25 | ) 26 | $(TR 27 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.nullType, `nullType`)) 28 | $(TD True if the resolved type is typeof(null)) 29 | ) 30 | $(TR 31 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.unaryOver, `unaryOver`)) 32 | $(TD True if the resolved type a unary funtion over some other types) 33 | ) 34 | $(TR 35 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.binaryOver, `binaryOver`)) 36 | $(TD True if the resolved type a binary funtion over some other types) 37 | ) 38 | $(TR 39 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.functionOver, `functionOver`)) 40 | $(TD True if the resolved type an n-ary funtion over n types) 41 | ) 42 | $(TR 43 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.refType, `refType`)) 44 | $(TD True if the resolved type a reference type) 45 | ) 46 | $(TR 47 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.valueType, `valueType`)) 48 | $(TD True if the resolved type a value type) 49 | ) 50 | $(TR 51 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.literalOf, `literalOf`)) 52 | $(TD True if the resolved type is a literal of a type of T) 53 | ) 54 | $(TR 55 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.literal, `literal`)) 56 | $(TD True if the resolved type is a literal) 57 | ) 58 | $(TR 59 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.copyConstructable, `copyConstructable`)) 60 | $(TD True if resolved type is copy constructable) 61 | ) 62 | $(TR 63 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.nonTriviallyCopyConstructable, `nonTriviallyCopyConstructable`)) 64 | $(TD True if resolved type is non-trivially copy constructable) 65 | ) 66 | $(TR 67 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.triviallyCopyConstructable, `triviallyCopyConstructable`)) 68 | $(TD True if resolved is trivially copy constructable) 69 | ) 70 | $(TR 71 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.equatableTo, `equatableTo`)) 72 | $(TD True if resolved type is equatabel to other) 73 | ) 74 | $(TR 75 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.nullTestable, `nullTestable`)) 76 | $(TD True if resolved type can be checked against null) 77 | ) 78 | $(TR 79 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.nullSettable, `nullSettable`)) 80 | $(TD True if resolved type can be set to null) 81 | ) 82 | $(TR 83 | $(TD $(DDOX_NAMED_REF bolts.iz.iz.refDecl, `refDecl`)) 84 | $(TD True if resolved type is declred with ref or is a function that returns ref) 85 | ) 86 | ) 87 | 88 | See_also: 89 | - https://dlang.org/spec/template.html#variadic-templates 90 | */ 91 | template iz(Aliases...) if (Aliases.length == 1) { 92 | import bolts.traits.symbols: TypesOf; 93 | 94 | alias ResolvedType = TypesOf!Aliases[0]; 95 | 96 | /// See: `bolts.traits.symbols.isOf` 97 | static template of(Other...) if (Other.length == 1) { 98 | enum of = from.bolts.traits.isOf!(Aliases[0], Other[0]); 99 | } 100 | 101 | // TODO: Remove on major bump 102 | deprecated("use nullTestable") 103 | enum nullable = from.bolts.traits.isNullable!ResolvedType; 104 | 105 | /// See: `bolts.traits.types.isNullType` 106 | enum nullType = from.bolts.traits.isNullType!ResolvedType; 107 | 108 | /// See: `bolts.traits.symbols.isSame` 109 | static template sameAs(Other...) if (Other.length == 1) { 110 | enum sameAs = from.bolts.traits.isSame!(Aliases[0], Other[0]); 111 | } 112 | 113 | /// See: `bolts.traits.functions.isFunctionOver` 114 | enum functionOver(U...) = from.bolts.traits.isFunctionOver!(Aliases[0], U); 115 | 116 | /// See: `bolts.traits.functions.isUnaryOver` 117 | enum unaryOver(U...) = from.bolts.traits.isUnaryOver!(Aliases[0], U); 118 | 119 | /// See: `bolts.traits.functions.isBinaryOver` 120 | enum binaryOver(U...) = from.bolts.traits.isBinaryOver!(Aliases[0], U); 121 | 122 | /// See: `bolts.traits.types.isRefType` 123 | enum refType = from.bolts.traits.isRefType!ResolvedType; 124 | 125 | /// See: `bolts.traits.types.isValueType` 126 | enum valueType = from.bolts.traits.isValueType!ResolvedType; 127 | 128 | /// See: `bolts.traits.symbols.isLiteralOf` 129 | enum literalOf(T) = from.bolts.traits.isLiteralOf!(Aliases[0], T); 130 | 131 | /// See: `bolts.traits.symbols.isLiteral` 132 | enum literal = from.bolts.traits.isLiteral!(Aliases[0]); 133 | 134 | /// See: `bolts.traits.types.isCopyConstructable` 135 | enum copyConstructable = from.bolts.traits.isCopyConstructable!ResolvedType; 136 | 137 | /// See: `bolts.traits.types.isNonTriviallyCopyConstructable` 138 | enum nonTriviallyCopyConstructable = from.bolts.traits.isNonTriviallyCopyConstructable!ResolvedType; 139 | 140 | /// See: `bolts.traits.types.isTriviallyCopyConstructable` 141 | enum triviallyCopyConstructable = from.bolts.traits.isTriviallyCopyConstructable!ResolvedType; 142 | 143 | /// See: `bolts.traits.types.areEquatable` 144 | static template equatableTo(Other...) if (Other.length == 1) { 145 | enum equatableTo = from.bolts.traits.areEquatable!(Aliases[0], Other[0]); 146 | } 147 | 148 | /// See: `bolts.traits.types.isNullTestable` 149 | enum nullTestable = from.bolts.traits.isNullTestable!Aliases; 150 | 151 | /// See: `bolts.traits.types.isNullSettable` 152 | enum nullSettable = from.bolts.traits.isNullSettable!Aliases; 153 | 154 | /// See: `bolts.traits.symbols.isRefDecl` 155 | enum refDecl = from.bolts.traits.isRefDecl!Aliases; 156 | } 157 | 158 | /// 159 | unittest { 160 | int i = 3; 161 | int j = 4; 162 | int *pi = null; 163 | 164 | // Is it resolved to the same type as another? 165 | static assert( iz!i.of!int); 166 | static assert(!iz!i.of!(int*)); 167 | static assert( iz!3.of!i); 168 | static assert(!iz!int.of!pi); 169 | 170 | // Is it the same as another? 171 | static assert( iz!i.sameAs!i); 172 | static assert(!iz!i.sameAs!j); 173 | static assert( iz!1.sameAs!1); 174 | static assert(!iz!1.sameAs!2); 175 | 176 | // Using std.meta algorithm with iz 177 | import std.meta: allSatisfy, AliasSeq; 178 | static assert(allSatisfy!(iz!int.of, 3, 4, int, i)); 179 | 180 | /// Is it a function over 181 | static assert( iz!(a => a).unaryOver!int); 182 | static assert( iz!((a, b) => a).binaryOver!(int, int)); 183 | static assert( iz!((a, b, c, d) => a).functionOver!(int, int, int, int)); 184 | 185 | // Is this thing a value or reference type? 186 | struct SValueType {} 187 | class CRefType {} 188 | static assert( iz!SValueType.valueType); 189 | static assert(!iz!CRefType.valueType); 190 | static assert(!iz!SValueType.refType); 191 | static assert( iz!CRefType.refType); 192 | 193 | static assert( iz!"hello".literalOf!string); 194 | static assert(!iz!3.literalOf!string); 195 | 196 | // Is this thing copy constructable? 197 | static struct SDisabledCopyConstructor { @disable this(ref typeof(this)); } 198 | static assert(!iz!SDisabledCopyConstructor.copyConstructable); 199 | static assert( iz!int.copyConstructable); 200 | 201 | // Does this thing define a custom copy constructor (i.e. non-trivial copy constructor) 202 | static struct SCopyConstructor { this(ref typeof(this)) {} } 203 | static assert( iz!SCopyConstructor.nonTriviallyCopyConstructable); 204 | static assert(!iz!SCopyConstructor.triviallyCopyConstructable); 205 | static assert(!iz!int.nonTriviallyCopyConstructable); 206 | static assert( iz!int.triviallyCopyConstructable); 207 | 208 | // Can we equate these things? 209 | static assert( iz!int.equatableTo!3); 210 | static assert(!iz!3.equatableTo!string); 211 | 212 | // What null-semantics does the type have 213 | 214 | // Is it settable to null? 215 | static struct SNullSettable { void opAssign(int*) {} } 216 | static assert( iz!pi.nullSettable); 217 | static assert( iz!SNullSettable.nullSettable); 218 | static assert(!iz!i.nullSettable); 219 | 220 | // Is it checable with null? (i.e. if (this is null) ) 221 | static assert( iz!pi.nullTestable); 222 | static assert(!iz!SNullSettable.nullTestable); 223 | static assert(!iz!i.nullTestable); 224 | 225 | // Is it typeof(null)? 226 | static assert(!iz!int.nullType); 227 | static assert( iz!null.nullType); 228 | static assert( iz!(typeof(null)).nullType); 229 | } 230 | -------------------------------------------------------------------------------- /source/bolts/members.d: -------------------------------------------------------------------------------- 1 | /** 2 | Provides compile time utilities that can query a type's members 3 | */ 4 | module bolts.members; 5 | 6 | import bolts.internal; 7 | 8 | private template AliasesOf(T, membersTuple...) { 9 | import std.meta: Alias, staticMap, ApplyLeft; 10 | alias getMember(T, string name) = Alias!(__traits(getMember, T, name)); 11 | alias mapMembers(T, names...) = staticMap!(ApplyLeft!(getMember, T), names); 12 | alias AliasesOf = mapMembers!(T, membersTuple); 13 | } 14 | 15 | /** 16 | Returns a list of member functions of T 17 | 18 | You can retireve them as a tuple of aliases, or strings 19 | */ 20 | template memberFunctionsOf(T) { 21 | import bolts.traits: hasFunctionMember; 22 | import bolts.meta: FilterMembersOf; 23 | 24 | /// Get as tuple of strings 25 | alias asStrings = FilterMembersOf!(T, hasFunctionMember); 26 | 27 | /// Get as a tuple of aliases 28 | alias asAliases = AliasesOf!(T, memberFunctionsOf!T.asStrings); 29 | } 30 | 31 | /// 32 | unittest { 33 | import std.meta: AliasSeq; 34 | struct A { 35 | void opCall() {} 36 | void g() {} 37 | } 38 | 39 | struct B { 40 | int m; 41 | A a; 42 | alias a this; 43 | void f0() {} 44 | void f1() {} 45 | } 46 | 47 | immutable array = [memberFunctionsOf!B.asStrings]; 48 | alias strings = memberFunctionsOf!B.asStrings; 49 | alias aliases = memberFunctionsOf!B.asAliases; 50 | 51 | static assert(array == ["f0", "f1"]); 52 | static assert(strings == AliasSeq!("f0", "f1")); 53 | 54 | static assert(is(typeof(array) == immutable string[])); 55 | static assert(is(typeof(strings) == AliasSeq!(string, string))); 56 | static assert(is(typeof(aliases) == AliasSeq!(typeof(B.f0), typeof(B.f1)))); 57 | } 58 | 59 | /** 60 | Returns a list of all the static members of a type 61 | 62 | You can retireve them as a sequence of aliases, or strings. 63 | 64 | See_Also: 65 | - https://forum.dlang.org/post/duvxnpwnuphuxlrkjplh@forum.dlang.org 66 | */ 67 | template staticMembersOf(T) { 68 | import std.traits: hasStaticMember; 69 | import bolts.meta: FilterMembersOf; 70 | 71 | /// Get as tuple of strings 72 | alias asStrings = FilterMembersOf!(T, hasStaticMember); 73 | 74 | /// Get as a tuple of aliases 75 | alias asAliases = AliasesOf!(T, staticMembersOf!T.asStrings); 76 | } 77 | 78 | /// 79 | unittest { 80 | import std.meta: AliasSeq, Alias; 81 | import bolts.traits: TypesOf; 82 | struct S { 83 | static void s0() {} 84 | static int s1 = 3; 85 | static immutable int s2 = 3; 86 | enum e = 9; 87 | void f() {} 88 | int i = 3; 89 | } 90 | 91 | immutable array = [staticMembersOf!S.asStrings]; 92 | alias strings = staticMembersOf!S.asStrings; 93 | alias aliases = staticMembersOf!S.asAliases; 94 | 95 | static assert(array == ["s0", "s1", "s2"]); 96 | static assert(strings == AliasSeq!("s0", "s1", "s2")); 97 | 98 | static assert(is(typeof(array) == immutable string[])); 99 | static assert(is(typeof(strings) == AliasSeq!(string, string, string))); 100 | static assert(is(typeof(aliases) == AliasSeq!(typeof(S.s0), typeof(S.s1), typeof(S.s2)))); 101 | } 102 | 103 | /** 104 | Used to extract details about a specific member 105 | 106 | Available member traits: 107 | $(LI `exists`) 108 | $(LI `self`) 109 | $(LI `isProperty`) 110 | $(LI `protection`) 111 | 112 | Params: 113 | Params[0] = type or alias to instance of a type 114 | Params[1] = name of member 115 | 116 | */ 117 | template member(Params...) if (Params.length == 2) { 118 | private enum name = Params[1]; 119 | private alias T = bolts.traits.TypesOf!Params[0]; 120 | private alias ResolvedType = ResolvePointer!T; 121 | 122 | /** 123 | True if the member field exists 124 | */ 125 | enum exists = __traits(hasMember, ResolvedType, name); 126 | 127 | /** 128 | Aliases to the member if it exists 129 | */ 130 | static if (exists) { 131 | alias self = __traits(getMember, ResolvedType, name); 132 | } else { 133 | template self() { 134 | static assert( 135 | 0, 136 | "Type '" ~ T.stringof ~ "' does not have member '" ~ name ~ "'." 137 | ); 138 | } 139 | } 140 | 141 | /** 142 | See: `bolts.traits.protectionLevel` 143 | */ 144 | static if (exists) { 145 | enum protection = from.bolts.traits.protectionLevel!self; 146 | } 147 | 148 | /** 149 | See: `bolts.traits.hasProperty` 150 | */ 151 | static if (exists) { 152 | enum isProperty = from.bolts.traits.hasProperty!(ResolvedType, name); 153 | } else { 154 | enum isProperty = false; 155 | } 156 | 157 | /** 158 | See `bolts.traits.propertySemantics` 159 | */ 160 | static if (exists && isProperty) { 161 | enum propertySemantics = from.bolts.traits.propertySemantics!self; 162 | } 163 | } 164 | 165 | /// 166 | unittest { 167 | import bolts.traits: ProtectionLevel, PropertySemantics; 168 | static struct S { 169 | public int publicI; 170 | protected int protectedI; 171 | private @property int readPropI() { return protectedI; } 172 | } 173 | 174 | // Check the protection level of various members 175 | static assert(member!(S, "publicI").protection == ProtectionLevel.public_); 176 | static assert(member!(S, "protectedI").protection == ProtectionLevel.protected_); 177 | static assert(member!(S, "readPropI").protection == ProtectionLevel.private_); 178 | 179 | // Check if any are properties 180 | static assert(!member!(S, "na").isProperty); 181 | static assert(!member!(S, "publicI").isProperty); 182 | static assert( member!(S, "readPropI").isProperty); 183 | 184 | // Check their semantics 185 | static assert(member!(S, "readPropI").propertySemantics == PropertySemantics.r); 186 | } 187 | -------------------------------------------------------------------------------- /source/bolts/meta.d: -------------------------------------------------------------------------------- 1 | /** 2 | Provides meta utilities that can modify types 3 | */ 4 | module bolts.meta; 5 | 6 | import bolts.internal; 7 | 8 | /** 9 | Flattens a list of types to their `ElementType` or in the case of an AliasPack it's `.UnpackDeep` 10 | 11 | If a type is a range then its `ElementType` is used 12 | */ 13 | template Flatten(Values...) { 14 | import std.meta: AliasSeq; 15 | static if (Values.length) { 16 | import std.range: isInputRange; 17 | import std.traits: isInstanceOf; 18 | 19 | alias Head = Values[0]; 20 | alias Tail = Values[1..$]; 21 | 22 | static if (isInstanceOf!(AliasPack, Head)) { 23 | alias Flatten = Flatten!(Head.UnpackDeep, Flatten!Tail); 24 | } else static if (isInputRange!Head) { 25 | import std.range: ElementType; 26 | alias Flatten = Flatten!(ElementType!Head, Flatten!Tail); 27 | } else { 28 | alias Flatten = AliasSeq!(Head, Flatten!Tail); 29 | } 30 | } else { 31 | alias Flatten = AliasSeq!(); 32 | } 33 | } 34 | 35 | /// 36 | unittest { 37 | import std.algorithm: filter; 38 | import std.meta: AliasSeq; 39 | 40 | alias R1 = typeof([1, 2, 3].filter!"true"); 41 | alias R2 = typeof([1.0, 2.0, 3.0]); 42 | 43 | static assert(is(Flatten!(int, double) == AliasSeq!(int, double))); 44 | static assert(is(Flatten!(int, R1, R2) == AliasSeq!(int, int, double))); 45 | static assert(is(Flatten!(R1, int) == AliasSeq!(int, int))); 46 | 47 | import std.traits: CommonType; 48 | static assert(is(CommonType!(Flatten!(int, R1, R2, float)) == double)); 49 | 50 | static assert(is(Flatten!(R1, int, AliasPack!(AliasPack!(float, int)), int) == AliasSeq!(int, int, float, int, int))); 51 | } 52 | 53 | /** 54 | Same as an AliasSeq that does not auto expand. 55 | 56 | You can get to the provided compile time sequence (AliasSeq) by accessing the `.Unpack` member. 57 | And if you want a recursive expansion there's `UnpackDeep` for that. Also a convenience 58 | `.equals!(otherAliasPack)` is provided. 59 | 60 | See_Also: 61 | - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org 62 | */ 63 | template AliasPack(T...) { 64 | alias Unpack = T; 65 | enum length = Unpack.length; 66 | 67 | private template UnpackDeepImpl(U...) { 68 | import std.meta: AliasSeq; 69 | static if (U.length) { 70 | import std.traits: isInstanceOf; 71 | static if (isInstanceOf!(AliasPack, U[0])) { 72 | alias Head = UnpackDeepImpl!(U[0].Unpack); 73 | } else { 74 | import std.meta: Alias; 75 | alias Head = Alias!(U[0]); 76 | } 77 | alias UnpackDeepImpl = AliasSeq!(Head, UnpackDeepImpl!(U[1 .. $])); 78 | } else { 79 | alias UnpackDeepImpl = AliasSeq!(); 80 | } 81 | } 82 | 83 | alias UnpackDeep = UnpackDeepImpl!T; 84 | 85 | template equals(U...) { 86 | static if (T.length == U.length) { 87 | static if (T.length == 0) { 88 | enum equals = true; 89 | } else { 90 | import bolts.traits: isSame; 91 | enum equals = isSame!(T[0], U[0]) && AliasPack!(T[1 .. $]).equals!(U[1 .. $]); 92 | } 93 | } else { 94 | enum equals = false; 95 | } 96 | } 97 | 98 | static if (length > 0) { 99 | import std.meta: Alias; 100 | alias Head = Alias!(T[0]); 101 | } else { 102 | alias Head = void; 103 | } 104 | 105 | static if (length > 1) { 106 | alias Tail = AliasPack!(T[1 .. $]); 107 | } else { 108 | alias Tail = AliasPack!(); 109 | } 110 | } 111 | 112 | /// 113 | unittest { 114 | alias P = AliasPack!(1, int, "abc"); 115 | static assert( P.equals!(1, int, "abc")); 116 | static assert(!P.equals!(1, int, "cba")); 117 | 118 | static assert(P.Head == 1); 119 | static assert(P.Tail.equals!(int, "abc")); 120 | static assert(is(P.Tail.Head == int)); 121 | static assert(P.Tail.Tail.Head == "abc"); 122 | 123 | alias PackOfPacks = AliasPack!(AliasPack!(1, int), AliasPack!(2, float), 17); 124 | static assert(AliasPack!(PackOfPacks.UnpackDeep).equals!(1, int, 2, float, 17)); 125 | } 126 | 127 | deprecated("use Zip instead") 128 | alias staticZip = Zip; 129 | 130 | /** 131 | Zips sequences of `AliasPack`s together into an AliasPack of AliasPacks. 132 | 133 | See_Also: 134 | - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org 135 | */ 136 | template Zip(Seqs...) { 137 | import std.traits: isInstanceOf; 138 | import std.meta: allSatisfy, staticMap; 139 | 140 | private enum isPack(alias T) = isInstanceOf!(AliasPack, T); 141 | 142 | static assert( 143 | Seqs.length >= 2 && allSatisfy!(isPack, Seqs), 144 | "Must have 2 or more arguments of type AliasPack" 145 | ); 146 | 147 | enum len = Seqs[0].length; 148 | static foreach (Seq; Seqs[1 .. $]) { 149 | static assert( 150 | Seq.length == len, 151 | "All arguments to Zip must have the same length" 152 | ); 153 | } 154 | 155 | alias Head(alias P) = P.Head; 156 | alias Tail(alias P) = P.Tail; 157 | 158 | static if (len == 0) { 159 | alias Zip = AliasPack!(); 160 | } else { 161 | alias Zip = AliasPack!( 162 | AliasPack!(staticMap!(Head, Seqs)), 163 | Zip!(staticMap!(Tail, Seqs)).Unpack 164 | ); 165 | } 166 | } 167 | 168 | /// 169 | unittest { 170 | alias a = AliasPack!(1, 2, 3); 171 | alias b = AliasPack!(4, 5, 6); 172 | alias c = AliasPack!(7, 8, 9); 173 | alias d = Zip!(a, b, c); 174 | 175 | static assert(d.length == 3); 176 | 177 | static assert(d.Unpack[0].equals!(1, 4, 7)); 178 | static assert(d.Unpack[1].equals!(2, 5, 8)); 179 | static assert(d.Unpack[2].equals!(3, 6, 9)); 180 | } 181 | 182 | /** 183 | Extract the elements of an `AliasPack` at given positions. 184 | 185 | The function takes two parameters - the first is the `AliasPack` to extract 186 | from, the second is an `AliasPack` of positions. 187 | */ 188 | template Pluck(alias Pack, size_t[] Positions) if (from.std.traits.isInstanceOf!(AliasPack, Pack)) { 189 | static if (Positions.length == 0) { 190 | alias Pluck = AliasPack!(); 191 | } else { 192 | static assert( 193 | Positions[0] >= 0 && Positions[0] < Pack.length, 194 | "Position is out of range" 195 | ); 196 | alias Pluck = AliasPack!( 197 | Pack.Unpack[Positions[0]], 198 | Pluck!(Pack, Positions[1..$]).Unpack 199 | ); 200 | } 201 | } 202 | 203 | /// 204 | unittest { 205 | static assert(Pluck!(AliasPack!(), []).equals!()); 206 | static assert(Pluck!(AliasPack!(int, char, float), []).equals!()); 207 | static assert(Pluck!(AliasPack!(int, char, float), [0, 2]).equals!(int, float)); 208 | } 209 | 210 | /** 211 | Filters all the members of a type based on a provided predicate 212 | 213 | The predicate takes two parameters - the first is the type, the second is 214 | the string name of the member being iterated on. 215 | */ 216 | template FilterMembersOf(T, alias Fn) { 217 | import std.meta: Filter, ApplyLeft; 218 | alias ResolvedType = ResolvePointer!T; 219 | alias FilterMembersOf = Filter!(ApplyLeft!(Fn, ResolvedType), __traits(allMembers, ResolvedType)); 220 | } 221 | 222 | /// 223 | unittest { 224 | import std.meta: AliasSeq; 225 | 226 | struct S { 227 | int i; 228 | float f; 229 | int i2; 230 | short s; 231 | } 232 | 233 | enum hasInt(T, string name) = is(typeof(__traits(getMember, T, name)) == int); 234 | static assert(FilterMembersOf!(S, hasInt) == AliasSeq!("i", "i2")); 235 | } 236 | 237 | /** 238 | Removes all the attributes from compile-time entity that is given as the only argument 239 | */ 240 | template RemoveAttributes(T...) { 241 | import std.traits: functionLinkage, SetFunctionAttributes, FunctionAttribute; 242 | alias U = from.bolts.traits.TypesOf!T[0]; 243 | alias RemoveAttributes = SetFunctionAttributes!(U, functionLinkage!U, FunctionAttribute.none); 244 | } 245 | 246 | /// 247 | unittest { 248 | void f0() @safe {} 249 | void f1() @system {} 250 | 251 | static assert(is(RemoveAttributes!f1 == RemoveAttributes!f0)); 252 | } 253 | -------------------------------------------------------------------------------- /source/bolts/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Contains a number of static reflection utilties that query compile time entities (traits) or transform 3 | them (meta). General utilties are in the modules `traits` and `meta` and most specific ones are in 4 | dedicated modules (i.e. `bolts.members` provides utilities over a type's members). 5 | 6 | Most functions here operate on any compile time entity. For example `isUnaryOver` works in both these situatons: 7 | --- 8 | int i; 9 | void f(int) {} 10 | isFunctionOver!(f, int); 11 | isFunctionOver!(f, 3); 12 | isFunctionOver!(f, i); 13 | --- 14 | 15 | Iz_Super_Template: 16 | 17 | The $(DDOX_NAMED_REF bolts.iz.iz, `iz`) super template. Has a lot of the traits on types encapulated in one place. So 18 | if there's a trait that tells you something about a compile time entity, chances are `iz` will have it. E.g: 19 | --- 20 | void f(int, float, string) {} 21 | iz!f.unaryOver!(int, float, string); 22 | iz!f.unaryOver!(3, float, ""); 23 | --- 24 | 25 | Member_Super_Template: 26 | 27 | The $(DDOX_NAMED_REF bolts.members.member, `member`) super template, found in the `bolts.members` module is similar to 28 | the $(DDOX_NAMED_REF bolts.iz.iz, `iz`) template but works on members of types only: 29 | 30 | --- 31 | import bolts.members: member; 32 | struct S { 33 | static void f() {} 34 | } 35 | assert(member!(S, "f").exists); 36 | assert(member!(S, "f").protection == ProtectionLevel.public_); 37 | assert(!member!(S, "f").isProperty); 38 | --- 39 | 40 | Signatures_(experimental): 41 | 42 | Signatures are a way to enforce types to comply with other types. For example if you are making a range you can ensure your types conform to a range by mixing in a `Models` template to the type that needs it. You can also use the utilities provided here to constrain functions to types that adhere to a specific signature. 43 | 44 | --- 45 | interface InputRange(T) { 46 | @property bool empty(); 47 | @property T front(); 48 | @ignoreAttributes void popFront(); 49 | } 50 | 51 | struct MyRange { 52 | mixin Models!(InputRange!int); 53 | } 54 | --- 55 | 56 | The above will fail to compile with something like: 57 | 58 | --- 59 | source/bolts/experimental/signatures.d(310,5): Error: static assert: "Type MyRange does not comply to signature InputRange!(int) 60 | Missing identifier empty of type bool. 61 | Missing identifier front of type int. 62 | Missing identifier popFront of function void(). 63 | source/bolts/experimental/signatures.d(464): <-- Signature InputRange!(int) defined here. 64 | source/bolts/experimental/signatures.d(471): <-- Checked here." 65 | --- 66 | 67 | Refraction_(experimental): 68 | 69 | It is sometimes necessary to create a function which is an exact copy of 70 | another function. Or sometimes it is necessary to introduce a few variations, 71 | while carrying all the other aspects. Because of function attributes, parameter 72 | storage classes and user-defined attributes, this requires building a string 73 | mixin. In addition, the mixed-in code must refer only to local names, if it is 74 | to work across module boundaires. This module facilitates the creation of such 75 | mixins. 76 | 77 | For example, this creates a function that has a different name and return type, 78 | but retains the 'pure' attribute from the original function: 79 | 80 | --- 81 | pure int answer() { return 42; } 82 | mixin( 83 | refract!(answer, "answer").withName("realAnswer") 84 | .withReturnType("real") 85 | .mixture); 86 | static assert(is(typeof(realAnswer()) == real)); 87 | static assert(functionAttributes!realAnswer & FunctionAttribute.pure_); 88 | --- 89 | 90 | 91 | All_the_things: 92 | 93 | $(TABLE 94 | $(TR $(TH Module) $(TH Function) $(TH Description)) 95 | $(TR 96 | $(TD `bolts.from`) 97 | $(TD $(DDOX_NAMED_REF bolts.from.from, `from`)) 98 | $(TD lazy import of symbols) 99 | ) 100 | $(TR 101 | $(TD `bolts.members`) 102 | $(TD $(DDOX_NAMED_REF bolts.members.memberFunctionsOf, `memberFunctionsOf`)) 103 | $(TD Returns a list of all member functions) 104 | ) 105 | $(TR 106 | $(TD) 107 | $(TD $(DDOX_NAMED_REF bolts.members.staticMembersOf, `staticMembersOf`)) 108 | $(TD Returns a list of of all static members) 109 | ) 110 | $(TR 111 | $(TD) 112 | $(TD $(DDOX_NAMED_REF bolts.members.member, `member`)) 113 | $(TD If a type has a member with certain attributes) 114 | ) 115 | $(TR 116 | $(TD) 117 | $(TD $(DDOX_NAMED_REF bolts.meta.Flatten, `Flatten`)) 118 | $(TD Takes a list of ranges and non ranges and returns a list of types of the ranges and types of the non ranges) 119 | ) 120 | $(TR 121 | $(TD) 122 | $(TD $(DDOX_NAMED_REF bolts.meta.AliasPack, `AliasPack`)) 123 | $(TD Represents an AliasSeq that is not auto expanded) 124 | ) 125 | $(TR 126 | $(TD) 127 | $(TD $(DDOX_NAMED_REF bolts.meta.Zip, `Zip`)) 128 | $(TD Zip m n-tuple `AliasPack`s together to form n m-tuple AliasPacks) 129 | ) 130 | $(TR 131 | $(TD) 132 | $(TD $(DDOX_NAMED_REF bolts.meta.Pluck, `Pluck`)) 133 | $(TD Extract `AliasPack` elements at positions) 134 | ) 135 | $(TR 136 | $(TD) 137 | $(TD $(DDOX_NAMED_REF bolts.meta.FilterMembersOf, `FilterMembersOf`)) 138 | $(TD Filters the members of a type based on a has-predicate) 139 | ) 140 | $(TR 141 | $(TD) 142 | $(TD $(DDOX_NAMED_REF bolts.meta.RemoveAttributes, `RemoveAttributes`)) 143 | $(TD Removes all the attributes of a symbol and returns the new type) 144 | ) 145 | $(TR 146 | $(TD `bolts.range`) 147 | $(TD $(DDOX_NAMED_REF bolts.range.isSortedRange, `isSortedRange`)) 148 | $(TD Tells you if a range is sorted) 149 | ) 150 | $(TR 151 | $(TD) 152 | $(TD $(DDOX_NAMED_REF bolts.range.sortingPredicate, `sortingPredicate`)) 153 | $(TD Can be used to extract the sorting predicate for a range) 154 | ) 155 | $(TR 156 | $(TD) 157 | $(TD $(DDOX_NAMED_REF bolts.range.CommonTypeOfRanges, `CommonTypeOfRanges`)) 158 | $(TD Finds the common type from a list of ranges) 159 | ) 160 | $(TR 161 | $(TD `bolts.iz`) 162 | $(TD $(DDOX_NAMED_REF bolts.iz.iz, `iz`)) 163 | $(TD Allows you to query a type or alias with a nicer syntax, i.e. `isNullSettable!T` == `iz!T.nullSettable`) 164 | ) 165 | $(TR 166 | $(TD `bolts.experimental.signatures`) 167 | $(TD $(DDOX_NAMED_REF bolts.experimental.signatures.isModelOf, `isModelOf`)) 168 | $(TD Allows you to check if a structure models another structure - i.e. enforces duck typing) 169 | ) 170 | $(TR 171 | $(TD) 172 | $(TD $(DDOX_NAMED_REF bolts.experimental.signatures.Models, `Models`)) 173 | $(TD Mixin that throws a compile error if a structure does not match another) 174 | ) 175 | $(TR 176 | $(TD `bolts.experimental.refraction`) 177 | $(TD $(DDOX_NAMED_REF bolts.experimental.refraction.refract, `refract`)) 178 | $(TD Returns a compile time object that helps create a string mixin corresponding to a function, possibly with variations) 179 | ) 180 | $(TR 181 | $(TD `bolts.traits`) 182 | $(TD $(DDOX_NAMED_REF bolts.traits.functions.isFunctionOver, `isFunctionOver`)) 183 | $(TD Checks if a function is n-ary over the passed in types) 184 | ) 185 | $(TR 186 | $(TD) 187 | $(TD $(DDOX_NAMED_REF bolts.traits.functions.isUnaryOver, `isUnaryOver`)) 188 | $(TD Checks if a function is unary over some type) 189 | ) 190 | $(TR 191 | $(TD) 192 | $(TD $(DDOX_NAMED_REF bolts.traits.functions.isBinaryOver, `isBinaryOver`)) 193 | $(TD Checks if a function is binary over some types) 194 | ) 195 | $(TR 196 | $(TD) 197 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.TypesOf, `TypesOf`)) 198 | $(TD Returns an AliasSeq of the types of all values given - values can be types or expressions) 199 | ) 200 | $(TR 201 | $(TD) 202 | $(TD $(DDOX_NAMED_REF bolts.traits.types.areCombinable, `areCombinable`)) 203 | $(TD Checks if a set of ranges and non ranges share a common type) 204 | ) 205 | $(TR 206 | $(TD) 207 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isProperty, `isProperty`)) 208 | $(TD Tells you if a symbol is an @property) 209 | ) 210 | $(TR 211 | $(TD) 212 | $(TD $(DDOX_NAMED_REF bolts.traits.has.hasProperty, `hasProperty`)) 213 | $(TD Tells you if a name is a member and property in a type) 214 | ) 215 | $(TR 216 | $(TD) 217 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.propertySemantics, `propertySemantics`)) 218 | $(TD Tells you if a property symbol has read and/or write semantics) 219 | ) 220 | $(TR 221 | $(TD) 222 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.protectionLevel, `protectionLevel`)) 223 | $(TD Returns the protection level for a symbol) 224 | ) 225 | $(TR 226 | $(TD) 227 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isManifestAssignable, `isManifestAssignable`)) 228 | $(TD If a member of a type can be assigned to a manifest constant) 229 | ) 230 | $(TR 231 | $(TD) 232 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isOf, `isOf`)) 233 | $(TD Is the resolved type is of another resolved type) 234 | ) 235 | $(TR 236 | $(TD) 237 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isNullType, `isNullType`)) 238 | $(TD If T is typeof(null)) 239 | ) 240 | $(TR 241 | $(TD) 242 | $(TD $(DDOX_NAMED_REF bolts.traits.types.StringOf, `StringOf`)) 243 | $(TD Stringifies a type, unlike `.stringof` this version doesn't spit out mangled gibberish) 244 | ) 245 | $(TR 246 | $(TD) 247 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isSame, `isSame`)) 248 | $(TD Returns true if a and b are the same thing - same type, same literal value, or same symbol) 249 | ) 250 | $(TR 251 | $(TD) 252 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isRefType, `isRefType`)) 253 | $(TD Checks if a compile time entity is a reference type) 254 | ) 255 | $(TR 256 | $(TD) 257 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isLiteralOf, `isLiteralOf`)) 258 | $(TD Checks if a compile time entity is a litera of a specific type) 259 | ) 260 | $(TR 261 | $(TD) 262 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isLiteral, `isLiteral`)) 263 | $(TD Checks if a compile time entity is a literal) 264 | ) 265 | $(TR 266 | $(TD) 267 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isCopyConstructable, `isCopyConstructable`)) 268 | $(TD Checks if a compile time entity is copy constructable) 269 | ) 270 | $(TR 271 | $(TD) 272 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isNonTriviallyCopyConstructable, `isNonTriviallyCopyConstructable`)) 273 | $(TD Checks if a compile time entity is non-trivially (i.e. user defined) copy constructable) 274 | ) 275 | $(TR 276 | $(TD) 277 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isTriviallyCopyConstructable, `isTriviallyCopyConstructable`)) 278 | $(TD Checks if a compile time entity is trivially copy constructable) 279 | ) 280 | $(TR 281 | $(TD) 282 | $(TD $(DDOX_NAMED_REF bolts.traits.has.hasFunctionMember, `hasFunctionMember`)) 283 | $(TD Checks if a type has a member that is a function) 284 | ) 285 | $(TR 286 | $(TD) 287 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isValueType, `isValueType`)) 288 | $(TD Checks if a compile time entity is a value type) 289 | ) 290 | $(TR 291 | $(TD) 292 | $(TD $(DDOX_NAMED_REF bolts.traits.types.areEquatable, `areEquatable`)) 293 | $(TD Returns true if two things are equatable) 294 | ) 295 | $(TR 296 | $(TD) 297 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isNullSettable, `isNullSettable`)) 298 | $(TD Check if a thing can be set to null) 299 | ) 300 | $(TR 301 | $(TD) 302 | $(TD $(DDOX_NAMED_REF bolts.traits.types.isNullTestable, `isNullTestable`)) 303 | $(TD Check if a thing can be checked to be null - i.e. if (thing is null) ) 304 | ) 305 | $(TR 306 | $(TD) 307 | $(TD $(DDOX_NAMED_REF bolts.traits.symbols.isRefDecl, `isRefDecl`)) 308 | $(TD See if a thing is declared as ref, or function returns by ref) 309 | ) 310 | ) 311 | */ 312 | module bolts; 313 | 314 | public { 315 | import bolts.traits; 316 | import bolts.meta; 317 | import bolts.range; 318 | import bolts.members; 319 | import bolts.iz; 320 | import bolts.from; 321 | import bolts.experimental; 322 | } 323 | -------------------------------------------------------------------------------- /source/bolts/range.d: -------------------------------------------------------------------------------- 1 | /** 2 | Static introspection utilties for ranges 3 | */ 4 | module bolts.range; 5 | 6 | import bolts.internal; 7 | import std.range : isInputRange; 8 | import std.meta : allSatisfy; 9 | 10 | /** 11 | True if R is a `SortedRange` 12 | 13 | See_Also: 14 | `std.range.SortedRange` 15 | */ 16 | template isSortedRange(R...) if (R.length == 1) { 17 | import std.range: SortedRange; 18 | import bolts.traits: TypesOf; 19 | alias T = TypesOf!R[0]; 20 | enum isSortedRange = is(T : SortedRange!U, U...); 21 | } 22 | 23 | /// 24 | unittest { 25 | import std.algorithm: sort; 26 | import std.range: assumeSorted; 27 | static assert(isSortedRange!(typeof([0, 1, 2])) == false); 28 | static assert(isSortedRange!([0, 1, 2].sort) == true); 29 | static assert(isSortedRange!(typeof([0, 1, 2].assumeSorted)) == true); 30 | static assert(isSortedRange!int == false); 31 | } 32 | 33 | /** 34 | Given a `SortedRange` R, `sortingPredicate!R(a, b)` will call in to the predicate 35 | that was used to create the `SortedRange` 36 | 37 | Params: 38 | Args[0] = the range to extract the predicate from 39 | Args[1] = the sorting predicate to fallback to if `Range` is not a `SortedRange` 40 | */ 41 | template sortingPredicate(Args...) 42 | if ((Args.length == 1 || Args.length == 2) && isInputRange!(from.bolts.traits.TypesOf!Args[0])) { 43 | import bolts.traits: TypesOf; 44 | import std.range: SortedRange; 45 | import std.functional: binaryFun; 46 | alias R = TypesOf!Args[0]; 47 | static if (is(R : SortedRange!P, P...)) { 48 | alias sortingPredicate = binaryFun!(P[1]); 49 | } else { 50 | static if (Args.length == 2) { 51 | alias pred = binaryFun!(Args[1]); 52 | } else { 53 | alias pred = (a, b) => a < b; 54 | } 55 | alias sortingPredicate = pred; 56 | } 57 | } 58 | 59 | /// 60 | unittest { 61 | import std.algorithm: sort; 62 | 63 | // As a type 64 | assert(sortingPredicate!(typeof([1].sort!"a < b"))(1, 2) == true); 65 | assert(sortingPredicate!(typeof([1].sort!((a, b) => a > b)))(1, 2) == false); 66 | 67 | // As a value 68 | assert(sortingPredicate!([1].sort!"a > b")(1, 2) == false); 69 | assert(sortingPredicate!([1].sort!((a, b) => a < b))(1, 2) == true); 70 | 71 | // Default predicate 72 | assert(sortingPredicate!(int[])(1, 2) == true); 73 | } 74 | 75 | /// Finds the CommonType of a list of ranges 76 | template CommonTypeOfRanges(Rs...) if (allSatisfy!(isInputRange, Rs)) { 77 | import std.traits: CommonType; 78 | import std.meta: staticMap; 79 | import std.range: ElementType; 80 | alias CommonTypeOfRanges = CommonType!(staticMap!(ElementType, Rs)); 81 | } 82 | 83 | /// 84 | unittest { 85 | auto a = [1, 2]; 86 | auto b = [1.0, 2.0]; 87 | static assert(is(CommonTypeOfRanges!(typeof(a), typeof(b)) == double)); 88 | } 89 | -------------------------------------------------------------------------------- /source/bolts/traits/functions.d: -------------------------------------------------------------------------------- 1 | /** 2 | Traits that can query information about functions 3 | */ 4 | module bolts.traits.functions; 5 | 6 | import bolts.internal; 7 | 8 | /** 9 | Returns true if the passed in function is an n-ary function over the next n parameter arguments 10 | 11 | Parameter arguments can be any compile time entity that can be typed. 12 | 13 | Params: 14 | T = The first argument is the function to check, the second are the types it should be called over 15 | */ 16 | template isFunctionOver(T...) { 17 | import std.meta: staticMap, Alias; 18 | import std.traits: isSomeFunction, Parameters; 19 | import bolts.meta: AliasPack, Zip; 20 | 21 | alias Types = from.bolts.traits.TypesOf!T; 22 | 23 | static if (Types.length >= 1) { 24 | alias DesiredParams = AliasPack!(Types[1 .. $]); 25 | static if (isSomeFunction!(Types[0])) { 26 | alias ExpectedParams = AliasPack!(Parameters!(Types[0])); 27 | static if (DesiredParams.length == ExpectedParams.length) { 28 | static if (DesiredParams.length == 0) { 29 | enum isFunctionOver = true; 30 | } else { 31 | import std.meta: allSatisfy; 32 | alias Pairs = Zip!(ExpectedParams, DesiredParams); 33 | // If is(DesiredType : ExpectedType) 34 | enum AreSame(alias Pair) = is(Pair.Unpack[1] : Pair.Unpack[0]); 35 | enum isFunctionOver = allSatisfy!(AreSame, Pairs.Unpack); 36 | } 37 | } else { 38 | enum isFunctionOver = false; 39 | } 40 | } else static if (is(Types[0] == void)) { 41 | // We're going to assume the first arg is a function literal ala lambda 42 | // And try and see if calling it with the init values of the desired 43 | // params works 44 | alias F = T[0]; 45 | alias Val(U) = Alias!(U.init); 46 | enum isFunctionOver = __traits(compiles, { 47 | auto tup = staticMap!(Val, DesiredParams.Unpack).init; 48 | F(tup); 49 | }); 50 | } else { 51 | enum isFunctionOver = false; 52 | } 53 | } else { 54 | enum isFunctionOver = false; 55 | } 56 | } 57 | 58 | /// 59 | unittest { 60 | int i; 61 | float f; 62 | void f0() {} 63 | void f1(int a) {} 64 | void f2(int a, string b) {} 65 | void f3(int a, string b, float c) {} 66 | 67 | static assert( isFunctionOver!(f0)); 68 | static assert(!isFunctionOver!(f0, int)); 69 | 70 | static assert(!isFunctionOver!(f1, string)); 71 | static assert( isFunctionOver!(f1, int)); 72 | static assert( isFunctionOver!(f1, i)); 73 | static assert(!isFunctionOver!(f1, f)); 74 | 75 | static assert(!isFunctionOver!(f2, int)); 76 | static assert( isFunctionOver!(f2, int, string)); 77 | static assert(!isFunctionOver!(f2, int, float)); 78 | static assert(!isFunctionOver!(f2, int, float, string)); 79 | 80 | static assert( isFunctionOver!(f3, int, string, float)); 81 | static assert(!isFunctionOver!(f3, int, float, string)); 82 | static assert(!isFunctionOver!(f3, int, float)); 83 | static assert(!isFunctionOver!(f3, int)); 84 | static assert(!isFunctionOver!(f3)); 85 | 86 | static assert( isFunctionOver!(() => 3)); 87 | static assert(!isFunctionOver!(() => 3, int)); 88 | static assert(!isFunctionOver!(a => a, float, int)); 89 | static assert( isFunctionOver!(a => a, float)); 90 | static assert( isFunctionOver!(a => a, int)); 91 | static assert( isFunctionOver!((int a) => a, short)); 92 | static assert(!isFunctionOver!((int a) => a, float)); 93 | static assert(!isFunctionOver!(a => a)); 94 | static assert( isFunctionOver!((a, b) => a + b, float, int)); 95 | 96 | struct A {} 97 | static assert(!isFunctionOver!((a, b) => a + b, A, int)); 98 | static assert( isFunctionOver!((a, b, c, d) => a+b+c+d, int, int, int ,int)); 99 | 100 | import std.functional: unaryFun; 101 | static assert( isFunctionOver!(unaryFun!"a", int)); 102 | static assert(!isFunctionOver!(unaryFun!"a", int, int)); 103 | 104 | import std.functional: binaryFun; 105 | static assert(!isFunctionOver!(binaryFun!"a", int)); 106 | static assert( isFunctionOver!(binaryFun!"a", int, int)); 107 | static assert( isFunctionOver!(binaryFun!"a > b", int, int)); 108 | static assert(!isFunctionOver!(binaryFun!"a > b", int, int, int)); 109 | 110 | class C {} 111 | class D : C {} 112 | void fc(C) {} 113 | void fd(D) {} 114 | static assert( isFunctionOver!(fc, C)); 115 | static assert( isFunctionOver!(fc, D)); 116 | static assert(!isFunctionOver!(fd, C)); 117 | static assert( isFunctionOver!(fd, D)); 118 | 119 | import std.math: ceil; 120 | static assert( isFunctionOver!(ceil, double)); 121 | static assert(!isFunctionOver!(ceil, double, double)); 122 | 123 | static assert(!isFunctionOver!(i)); 124 | static assert(!isFunctionOver!(i, int)); 125 | } 126 | 127 | unittest { 128 | struct S {} 129 | static assert(isFunctionOver!((ref s) => s, S)); 130 | } 131 | 132 | unittest { 133 | import std.algorithm: move; 134 | struct S { @disable this(); @disable void opAssign(S); @disable this(this); } 135 | static assert(isFunctionOver!((ref s) => s.move, S)); 136 | } 137 | 138 | /** 139 | Returns true if the first argument is a unary function over the next parameter argument 140 | 141 | Parameter arguments can be any compile time entity that can be typed. And the first argument can be a string 142 | that is also accpeted by `std.functional.binaryFun` in Phobos 143 | */ 144 | template isUnaryOver(T...) { 145 | import std.functional: unaryFun; 146 | enum isUnaryOver = T.length == 2 && (isFunctionOver!T || is(typeof(unaryFun!(T[0])(T[1].init)))); 147 | } 148 | 149 | /// 150 | unittest { 151 | void f0() {} 152 | void f1(int a) {} 153 | void f2(int a, int b) {} 154 | 155 | static assert(!isUnaryOver!(null, int)); 156 | static assert( isUnaryOver!((a => a), int)); 157 | static assert(!isUnaryOver!((a, b) => a + b, int)); 158 | 159 | static assert(!isUnaryOver!(f0, int)); 160 | static assert( isUnaryOver!(f1, int)); 161 | static assert(!isUnaryOver!(f2, int)); 162 | 163 | static assert( isUnaryOver!(f1, 3)); 164 | static assert(!isUnaryOver!(f1, "ff")); 165 | static assert(!isUnaryOver!(f1, 3, 4)); 166 | static assert( isUnaryOver!(typeof(f1), 3)); 167 | static assert(!isUnaryOver!(typeof(f1), "ff")); 168 | } 169 | 170 | /** 171 | Returns true if the first argument is a binary function over the next one OR two parameter arguments 172 | 173 | If one parameter is provided then it's duplicated. 174 | 175 | Parameter arguments can be any compile time entity that can be typed. And the first argument can be a string 176 | that is also accpeted by `std.functional.binaryFun` in Phobos 177 | */ 178 | template isBinaryOver(T...) { 179 | import std.functional: binaryFun; 180 | static if (T.length == 2) { 181 | enum isBinaryOver = isBinaryOver!(T[0], T[1], T[1]); 182 | } else { 183 | enum isBinaryOver = T.length == 3 && (isFunctionOver!T || is(typeof(binaryFun!(T[0])(T[1].init, T[2].init)))); 184 | } 185 | } 186 | 187 | /// 188 | unittest { 189 | void f0() {} 190 | void f1(int a) {} 191 | void f2(int a, int b) {} 192 | 193 | static assert(!isBinaryOver!(null, int)); 194 | static assert(!isBinaryOver!((a => a), int)); 195 | static assert( isBinaryOver!((a, b) => a + b, int)); 196 | static assert( isBinaryOver!((a, b) => a + b, int, int)); 197 | 198 | static assert(!isBinaryOver!(f0, int)); 199 | static assert(!isBinaryOver!(f1, int)); 200 | static assert( isBinaryOver!(f2, int)); 201 | static assert( isBinaryOver!(f2, int, int)); 202 | static assert(!isBinaryOver!(f2, int, string)); 203 | static assert(!isBinaryOver!(f2, int, int, int)); 204 | 205 | static assert( isBinaryOver!(f2, 3)); 206 | static assert( isBinaryOver!(f2, 3, 3)); 207 | static assert( isBinaryOver!(f2, 3, int)); 208 | } 209 | -------------------------------------------------------------------------------- /source/bolts/traits/has.d: -------------------------------------------------------------------------------- 1 | /** 2 | Traits that tell you if one thing has another thing 3 | */ 4 | module bolts.traits.has; 5 | 6 | import bolts.internal; 7 | 8 | /** 9 | Returns true if a type has a function member 10 | */ 11 | template hasFunctionMember(T, string name) { 12 | import std.traits: hasMember; 13 | static if (hasMember!(T, name)) { 14 | import std.traits: isFunction; 15 | enum hasFunctionMember = isFunction!(__traits(getMember, T, name)); 16 | } else { 17 | enum hasFunctionMember = false; 18 | } 19 | } 20 | 21 | /// 22 | unittest { 23 | static struct S { 24 | int i; 25 | void f0() {} 26 | int f1(int, int) { return 0; } 27 | static void f2(string) {} 28 | static int s; 29 | } 30 | 31 | static assert(!hasFunctionMember!(S, "i")); 32 | static assert( hasFunctionMember!(S, "f0")); 33 | static assert( hasFunctionMember!(S, "f1")); 34 | static assert( hasFunctionMember!(S, "f2")); 35 | static assert(!hasFunctionMember!(S, "s")); 36 | } 37 | 38 | /** 39 | Tells you if a name is a member and property in a type 40 | */ 41 | template hasProperty(T, string name) { 42 | import std.meta: anySatisfy; 43 | import std.traits: hasMember, isFunction; 44 | import bolts.traits.symbols: isProperty; 45 | 46 | alias ResolvedType = ResolvePointer!T; 47 | 48 | static if (hasMember!(ResolvedType, name) && isFunction!(__traits(getMember, ResolvedType, name))) { 49 | enum hasProperty = anySatisfy!(isProperty, __traits(getOverloads, ResolvedType, name)); 50 | } else { 51 | enum hasProperty = false; 52 | } 53 | } 54 | 55 | /// 56 | unittest { 57 | struct S { 58 | int m; 59 | static int sm; 60 | void f() {} 61 | static void sf() {} 62 | @property int rp() { return m; } 63 | @property void wp(int) {} 64 | } 65 | 66 | static assert(!hasProperty!(S, "na")); 67 | static assert(!hasProperty!(S, "m")); 68 | static assert(!hasProperty!(S, "sm")); 69 | static assert(!hasProperty!(S, "f")); 70 | static assert(!hasProperty!(S, "sf")); 71 | static assert( hasProperty!(S, "rp")); 72 | static assert( hasProperty!(S, "wp")); 73 | } 74 | 75 | unittest { 76 | struct S { 77 | int m; 78 | static int sm; 79 | void f() {} 80 | static void sf() {} 81 | @property int rp() { return m; } 82 | @property void wp(int) {} 83 | } 84 | 85 | static assert(!hasProperty!(S*, "na")); 86 | static assert(!hasProperty!(S*, "m")); 87 | static assert(!hasProperty!(S*, "sm")); 88 | static assert(!hasProperty!(S*, "f")); 89 | static assert(!hasProperty!(S*, "sf")); 90 | static assert( hasProperty!(S*, "rp")); 91 | static assert( hasProperty!(S*, "wp")); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /source/bolts/traits/package.d: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | Provides utilites that allow you to determine type traits 4 | */ 5 | module bolts.traits; 6 | 7 | /// 8 | unittest { 9 | class C {} 10 | struct S {} 11 | struct S1 { 12 | this(typeof(null)) {} 13 | void opAssign(typeof(null)) {} 14 | } 15 | 16 | static assert( isNullSettable!C); 17 | static assert(!isNullSettable!S); 18 | static assert( isNullSettable!S1); 19 | static assert( isNullSettable!(int *)); 20 | static assert(!isNullSettable!(int)); 21 | } 22 | 23 | public { 24 | import bolts.traits.functions; 25 | import bolts.traits.has; 26 | import bolts.traits.symbols; 27 | import bolts.traits.types; 28 | } 29 | -------------------------------------------------------------------------------- /source/bolts/traits/symbols.d: -------------------------------------------------------------------------------- 1 | /** 2 | Traits about symbols 3 | */ 4 | module bolts.traits.symbols; 5 | 6 | import bolts.internal; 7 | 8 | /** 9 | Returns the types of all values given. 10 | 11 | $(OD If isFunction!T then the typeof the address is taken if possible) 12 | $(OD If typeof(T) can be taken it is) 13 | $(OD Else it is appended on as is) 14 | 15 | Returns: 16 | AliasSeq of the resulting types 17 | */ 18 | template TypesOf(Symbols...) { 19 | import std.meta: AliasSeq; 20 | import std.traits: isExpressions, isFunction; 21 | static if (Symbols.length) { 22 | static if (isFunction!(Symbols[0]) && is(typeof(&Symbols[0]) F)) { 23 | alias T = F; 24 | } else static if (is(typeof(Symbols[0]))) { 25 | alias T = typeof(Symbols[0]); 26 | } else { 27 | alias T = Symbols[0]; 28 | } 29 | alias TypesOf = AliasSeq!(T, TypesOf!(Symbols[1..$])); 30 | } else { 31 | alias TypesOf = AliasSeq!(); 32 | } 33 | } 34 | 35 | /// 36 | unittest { 37 | import std.meta: AliasSeq; 38 | static assert(is(TypesOf!("hello", 1, 2, 3.0, real) == AliasSeq!(string, int, int, double, real))); 39 | } 40 | 41 | unittest { 42 | import std.meta: AliasSeq; 43 | static void f0() {} 44 | void f1() {} 45 | struct S { void f2() {} } 46 | static assert(is(TypesOf!(f0, f1, S.f2) == AliasSeq!(typeof(&f0), typeof(&f1), typeof(&S.f2)))); 47 | } 48 | 49 | unittest { 50 | import std.meta: AliasSeq; 51 | int f(int p) { return 3; } 52 | static assert(is(TypesOf!(typeof(f)) == AliasSeq!(typeof(f)))); 53 | } 54 | 55 | /** 56 | Checks if the resolved type of one thing is the same as the resolved type of another thing. 57 | 58 | If the type is callable, then `std.traits.ReturnType` is the resolved type 59 | */ 60 | template isOf(ab...) if (ab.length == 2) { 61 | alias Ts = TypesOf!ab; 62 | template resolve(T) { 63 | import std.traits: isCallable, ReturnType; 64 | static if (isCallable!T) { 65 | alias resolve = ReturnType!T; 66 | } else { 67 | alias resolve = T; 68 | } 69 | } 70 | 71 | enum isOf = is(resolve!(Ts[0]) == resolve!(Ts[1])); 72 | } 73 | 74 | /// 75 | unittest { 76 | static assert( isOf!(int, 3)); 77 | static assert( isOf!(7, 3)); 78 | static assert( isOf!(3, int)); 79 | static assert(!isOf!(float, 3)); 80 | static assert(!isOf!(float, string)); 81 | static assert(!isOf!(string, 3)); 82 | 83 | string tostr() { return ""; } 84 | static assert( isOf!(string, tostr)); 85 | } 86 | 87 | /** 88 | Returns true if a and b are the same thing, or false if not. Both a and b can be types, literals, or symbols. 89 | 90 | $(LI If both are types: `is(a == b)`) 91 | $(LI If both are literals: `a == b`) 92 | $(LI Else: `__traits(isSame, a, b)`) 93 | */ 94 | template isSame(ab...) if (ab.length == 2) { 95 | 96 | private static template expectType(T) {} 97 | private static template expectBool(bool b) {} 98 | 99 | static if (__traits(compiles, expectType!(ab[0]), expectType!(ab[1]))) { 100 | enum isSame = is(ab[0] == ab[1]); 101 | } else static if (!__traits(compiles, expectType!(ab[0])) 102 | && !__traits(compiles, expectType!(ab[1])) 103 | && __traits(compiles, expectBool!(ab[0] == ab[1])) 104 | ) { 105 | static if (!__traits(compiles, &ab[0]) || !__traits(compiles, &ab[1])) 106 | enum isSame = (ab[0] == ab[1]); 107 | else 108 | enum isSame = __traits(isSame, ab[0], ab[1]); 109 | } else { 110 | enum isSame = __traits(isSame, ab[0], ab[1]); 111 | } 112 | } 113 | 114 | /// 115 | unittest { 116 | static assert( isSame!(int, int)); 117 | static assert(!isSame!(int, short)); 118 | 119 | enum a = 1, b = 1, c = 2, s = "a", t = "a"; 120 | static assert( isSame!(1, 1)); 121 | static assert( isSame!(a, 1)); 122 | static assert( isSame!(a, b)); 123 | static assert(!isSame!(b, c)); 124 | static assert( isSame!("a", "a")); 125 | static assert( isSame!(s, "a")); 126 | static assert( isSame!(s, t)); 127 | static assert(!isSame!(s, "g")); 128 | static assert(!isSame!(1, "1")); 129 | static assert(!isSame!(a, "a")); 130 | static assert( isSame!(isSame, isSame)); 131 | static assert(!isSame!(isSame, a)); 132 | 133 | static assert(!isSame!(byte, a)); 134 | static assert(!isSame!(short, isSame)); 135 | static assert(!isSame!(a, int)); 136 | static assert(!isSame!(long, isSame)); 137 | 138 | static immutable X = 1, Y = 1, Z = 2; 139 | static assert( isSame!(X, X)); 140 | static assert(!isSame!(X, Y)); 141 | static assert(!isSame!(Y, Z)); 142 | 143 | int foo(); 144 | int bar(); 145 | real baz(int); 146 | static assert( isSame!(foo, foo)); 147 | static assert(!isSame!(foo, bar)); 148 | static assert(!isSame!(bar, baz)); 149 | static assert( isSame!(baz, baz)); 150 | static assert(!isSame!(foo, 0)); 151 | 152 | int x, y; 153 | real z; 154 | static assert( isSame!(x, x)); 155 | static assert(!isSame!(x, y)); 156 | static assert(!isSame!(y, z)); 157 | static assert( isSame!(z, z)); 158 | static assert(!isSame!(x, 0)); 159 | } 160 | 161 | /** 162 | Returns true if the argument is a manifest constant, built-in type field, or immutable static 163 | */ 164 | template isManifestAssignable(alias sym) { 165 | enum isManifestAssignable = __traits(compiles, { enum y = sym; } ); 166 | } 167 | 168 | /// 169 | unittest { 170 | struct A { 171 | int m; 172 | static immutable int sim = 1; 173 | enum e = 1; 174 | } 175 | 176 | static assert(!isManifestAssignable!(A.m)); 177 | static assert( isManifestAssignable!(A.e)); 178 | static assert( isManifestAssignable!(A.sim)); 179 | static assert(!isManifestAssignable!int); 180 | } 181 | 182 | /** 183 | Tells you if a symbol is an @property 184 | */ 185 | template isProperty(alias sym) { 186 | import std.traits: isFunction; 187 | import std.algorithm: canFind; 188 | static if (isFunction!sym) { 189 | enum isProperty = [__traits(getFunctionAttributes, sym)].canFind("@property"); 190 | } else { 191 | enum isProperty = false; 192 | } 193 | } 194 | 195 | /// 196 | unittest { 197 | int i; 198 | @property void f() {} 199 | 200 | struct S { 201 | int i; 202 | @property void f(int i) {} 203 | } 204 | 205 | static assert(!isProperty!(i)); 206 | static assert( isProperty!(f)); 207 | static assert(!isProperty!(S.i)); 208 | static assert( isProperty!(S.f)); 209 | } 210 | 211 | /** 212 | Represents the semantics supported by an @property member 213 | */ 214 | enum PropertySemantics { 215 | /// is a read property 216 | r, 217 | /// is a write property 218 | w, 219 | /// is a read-write property 220 | rw 221 | } 222 | 223 | /** 224 | Tells you what property semantics a symbol has 225 | 226 | Returns: 227 | The `PropertySemantics` of the symbol 228 | */ 229 | template propertySemantics(alias sym) if (isProperty!sym) { 230 | import std.array: split; 231 | import std.meta: anySatisfy; 232 | import std.traits: fullyQualifiedName, Parameters; 233 | 234 | private string lastPart(string str) { 235 | return str.split(".")[$ - 1]; 236 | } 237 | 238 | private static immutable name = fullyQualifiedName!sym.lastPart; 239 | 240 | static if (is(__traits(parent, sym) T)) { 241 | // Parent is a type, i.e. not a module 242 | private alias overloads = __traits(getOverloads, T, name); 243 | } else { 244 | // Parent is a module 245 | private alias overloads = __traits(getOverloads, __traits(parent, sym), name); 246 | } 247 | 248 | private enum isRead(alias s) = Parameters!s.length == 0; 249 | private enum isWrite(alias s) = Parameters!s.length > 0; 250 | 251 | private enum canRead = anySatisfy!(isRead, overloads); 252 | private enum canWrite = anySatisfy!(isWrite, overloads); 253 | 254 | static if (canWrite && canRead) { 255 | enum propertySemantics = PropertySemantics.rw; 256 | } else static if (canWrite) { 257 | enum propertySemantics = PropertySemantics.w; 258 | } else { 259 | enum propertySemantics = PropertySemantics.r; 260 | } 261 | } 262 | 263 | version (unittest) { 264 | private @property int localRead() {return 9;} 265 | private @property void localWrite(int i, int g) {} 266 | private @property int localReadWrite() {return 9;} 267 | private @property void localReadWrite(int i) {} 268 | } 269 | 270 | /// 271 | unittest { 272 | static assert(propertySemantics!localRead == PropertySemantics.r); 273 | static assert(propertySemantics!localWrite == PropertySemantics.w); 274 | static assert(propertySemantics!localReadWrite == PropertySemantics.rw); 275 | 276 | struct S { 277 | int m; 278 | @property int rp() { return m; } 279 | @property void wp(int) {} 280 | @property int rwp() { return m; } 281 | @property void rwp(int) {} 282 | } 283 | 284 | static assert(!__traits(compiles, propertySemantics!(S.na))); 285 | static assert(!__traits(compiles, propertySemantics!(S.m))); 286 | static assert(propertySemantics!(S.rp) == PropertySemantics.r); 287 | static assert(propertySemantics!(S.wp) == PropertySemantics.w); 288 | static assert(propertySemantics!(S.rwp) == PropertySemantics.rw); 289 | } 290 | 291 | /** 292 | Lists the various protection levels that can be applied on types 293 | */ 294 | enum ProtectionLevel : string { 295 | public_ = "public", 296 | protected_ = "protected", 297 | private_ = "private", 298 | } 299 | 300 | /** 301 | Check if the access level of any symbol 302 | */ 303 | template protectionLevel(symbol...) if (symbol.length == 1) { 304 | enum protectionLevel = cast(ProtectionLevel)__traits(getProtection, symbol[0]); 305 | } 306 | 307 | version (unittest) { 308 | protected immutable int protectedImmutableInt = 7; 309 | } 310 | 311 | /// 312 | unittest { 313 | import std.meta: AliasSeq; 314 | 315 | static assert(protectionLevel!protectedImmutableInt == ProtectionLevel.protected_); 316 | 317 | struct S { 318 | int i; 319 | public int m0; 320 | protected int m1; 321 | private int m2; 322 | } 323 | 324 | static assert(protectionLevel!(S.i) == ProtectionLevel.public_); 325 | static assert(protectionLevel!(S.m0) == ProtectionLevel.public_); 326 | static assert(protectionLevel!(S.m0) != ProtectionLevel.protected_); 327 | static assert(protectionLevel!(S.m1) == ProtectionLevel.protected_); 328 | static assert(protectionLevel!(S.m1) != ProtectionLevel.public_); 329 | static assert(protectionLevel!(S.m2) == ProtectionLevel.private_); 330 | static assert(protectionLevel!(S.m2) != ProtectionLevel.public_); 331 | } 332 | 333 | /** 334 | Checks if an alias is a literal of some type 335 | */ 336 | template isLiteralOf(ab...) if (ab.length == 2) { 337 | enum isLiteralOf = !is(typeof(&ab[0])) 338 | && is(typeof(ab[0])) 339 | && is(typeof(ab[0]) == ab[1]); 340 | } 341 | 342 | /// 343 | unittest { 344 | static assert( isLiteralOf!("hi", string)); 345 | static assert(!isLiteralOf!(3, string)); 346 | static assert( isLiteralOf!(3, int)); 347 | 348 | int a; 349 | static assert(!isLiteralOf!(a, int)); 350 | 351 | void f() {} 352 | static assert(!isLiteralOf!(f, string)); 353 | } 354 | 355 | /** 356 | Checks if an alias is a literal 357 | */ 358 | enum isLiteral(T...) = __traits(compiles, { enum x = T[0]; } ); 359 | 360 | /// 361 | unittest { 362 | int a; 363 | void f() {} 364 | assert( isLiteral!3); 365 | assert( isLiteral!"hi"); 366 | assert(!isLiteral!int); 367 | assert(!isLiteral!a); 368 | assert(!isLiteral!f); 369 | } 370 | 371 | /** 372 | True if it's a ref decleration. This applies to parameters and functions 373 | */ 374 | template isRefDecl(T...) if (T.length == 1) { 375 | import std.traits: isFunction; 376 | import std.algorithm: canFind; 377 | static if (isFunction!T) { 378 | enum isRefDecl = [__traits(getFunctionAttributes, T[0])].canFind("ref"); 379 | } else { 380 | enum isRefDecl = __traits(isRef, T[0]); 381 | } 382 | } 383 | 384 | /// 385 | unittest { 386 | bool checkRefParam()(auto ref int i) { 387 | return isRefDecl!i; 388 | } 389 | 390 | bool checkRefReturn() { 391 | ref int f(ref int i) { 392 | return i; 393 | } 394 | return isRefDecl!f; 395 | } 396 | 397 | bool checkMemberFunciton() { 398 | static struct S { 399 | int i; 400 | ref int value() return { return i; } 401 | } 402 | S s; 403 | return isRefDecl!(s.value) && isRefDecl!(S.value); 404 | } 405 | 406 | int i; 407 | assert( checkRefParam(i)); 408 | static assert(!checkRefParam(3)); 409 | static assert( checkRefReturn()); 410 | static assert( checkMemberFunciton()); 411 | static assert(!isRefDecl!int); 412 | static assert(!isRefDecl!i); 413 | } 414 | -------------------------------------------------------------------------------- /source/bolts/traits/types.d: -------------------------------------------------------------------------------- 1 | /** 2 | Traits that can query type information about stuff. 3 | */ 4 | module bolts.traits.types; 5 | 6 | import bolts.internal; 7 | 8 | /// True if `T` is of type null 9 | template isNullType(T...) if (T.length == 1) { 10 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 11 | enum isNullType = is(U == typeof(null)); 12 | } 13 | 14 | /// 15 | unittest { 16 | int a; 17 | int *b = null; 18 | struct C {} 19 | C c; 20 | void f() {} 21 | static assert(isNullType!null); 22 | static assert(isNullType!a == false); 23 | static assert(isNullType!b == false); 24 | static assert(isNullType!c == false); 25 | static assert(isNullType!f == false); 26 | } 27 | 28 | /** 29 | Checks if a compile time entity is a reference type. 30 | */ 31 | template isRefType(T...) if (T.length == 1) { 32 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 33 | enum isRefType = is(U == class) || is(U == interface); 34 | } 35 | 36 | /// 37 | unittest { 38 | struct S {} 39 | class C {} 40 | static assert(!isRefType!S); 41 | static assert(!isRefType!int); 42 | static assert( isRefType!C); 43 | } 44 | 45 | /** 46 | Checks if a compile time entity is a value type 47 | */ 48 | enum isValueType(T...) = !isRefType!T; 49 | 50 | /// 51 | unittest { 52 | struct S {} 53 | class C {} 54 | static assert( isValueType!int); 55 | static assert( isValueType!(int*)); 56 | static assert( isValueType!S); 57 | static assert(!isValueType!C); 58 | } 59 | 60 | /** 61 | Checks to see if a type is copy constructable - postblit doesn't count. 62 | 63 | Returns false if there's a user defined postblit. 64 | */ 65 | template isCopyConstructable(T...) if (T.length == 1) { 66 | import std.traits: hasMember; 67 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 68 | enum isCopyConstructable 69 | = __traits(compiles, { auto r = U.init; U copy = r; }) 70 | && !hasMember!(U, "__xpostblit"); 71 | } 72 | 73 | /// 74 | unittest { 75 | mixin copyConstructableKinds; 76 | 77 | static assert( isCopyConstructable!KindPOD); 78 | static assert( isCopyConstructable!KindHasCopyContrustor); 79 | static assert(!isCopyConstructable!KindHasPostBlit); 80 | static assert( isCopyConstructable!KindContainsPOD); 81 | static assert( isCopyConstructable!KindContainsTypeWithNonTrivialCopyConstructor); 82 | static assert(!isCopyConstructable!KindContainsTypeWithPostBlit); 83 | } 84 | 85 | /** 86 | Checks to see if a type is non-trivially copy constructable 87 | 88 | This does not check for postblits 89 | */ 90 | template isNonTriviallyCopyConstructable(T...) if (T.length == 1) { 91 | import std.traits: hasMember; 92 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 93 | enum isNonTriviallyCopyConstructable 94 | = isCopyConstructable!U 95 | && hasMember!(U, "__ctor"); 96 | } 97 | 98 | /// 99 | unittest { 100 | mixin copyConstructableKinds; 101 | 102 | static assert(!isNonTriviallyCopyConstructable!KindPOD); 103 | static assert( isNonTriviallyCopyConstructable!KindHasCopyContrustor); 104 | static assert(!isNonTriviallyCopyConstructable!KindHasPostBlit); 105 | static assert(!isNonTriviallyCopyConstructable!KindContainsPOD); 106 | static assert( isNonTriviallyCopyConstructable!KindContainsTypeWithNonTrivialCopyConstructor); 107 | static assert(!isNonTriviallyCopyConstructable!KindContainsTypeWithPostBlit); 108 | } 109 | 110 | /** 111 | Checks if a type is trivially constructable, that is no user-defined copy constructor exists - postblit doesn't count. 112 | */ 113 | template isTriviallyCopyConstructable(T...) if (T.length == 1) { 114 | import std.traits: hasMember; 115 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 116 | enum isTriviallyCopyConstructable 117 | = isCopyConstructable!U 118 | && !hasMember!(U, "__ctor"); 119 | } 120 | 121 | /// 122 | unittest { 123 | mixin copyConstructableKinds; 124 | 125 | static assert( isTriviallyCopyConstructable!KindPOD); 126 | static assert(!isTriviallyCopyConstructable!KindHasCopyContrustor); 127 | static assert(!isTriviallyCopyConstructable!KindHasPostBlit); 128 | static assert( isTriviallyCopyConstructable!KindContainsPOD); 129 | static assert(!isTriviallyCopyConstructable!KindContainsTypeWithNonTrivialCopyConstructor); 130 | static assert(!isTriviallyCopyConstructable!KindContainsTypeWithPostBlit); 131 | } 132 | 133 | /** 134 | Tells you if a list of types, which are composed of ranges and non ranges, 135 | share a common type after flattening the ranges (i.e. `ElementType`) 136 | 137 | This basically answers the question: $(Can I combine these ranges and values 138 | into a single range of a common type?) 139 | 140 | See_also: 141 | `bolts.meta.Flatten` 142 | */ 143 | template areCombinable(Values...) { 144 | import std.traits: CommonType; 145 | import bolts.meta: Flatten; 146 | enum areCombinable = !is(CommonType!(Flatten!Values) == void); 147 | } 148 | 149 | /// 150 | unittest { 151 | static assert(areCombinable!(int, int, int)); 152 | static assert(areCombinable!(float[], int, char[])); 153 | static assert(areCombinable!(string, int, int)); 154 | // Works with string because: 155 | import std.traits: CommonType; 156 | import std.range: ElementType; 157 | static assert(is(CommonType!(ElementType!string, int) == uint)); 158 | 159 | struct A {} 160 | static assert(!areCombinable!(A, int, int)); 161 | static assert(!areCombinable!(A[], int[])); 162 | static assert( areCombinable!(A[], A[])); 163 | static assert( areCombinable!(A[], A[], A)); 164 | static assert(!areCombinable!(int[], A)); 165 | } 166 | 167 | /** 168 | Returns true if two things are equatable 169 | */ 170 | template areEquatable(T...) if (T.length == 2) { 171 | private alias ResolvedTypes = from.bolts.traits.symbols.TypesOf!T; 172 | enum areEquatable = is(typeof(ResolvedTypes[0].init == ResolvedTypes[1].init)); 173 | } 174 | 175 | /// 176 | unittest { 177 | static assert( areEquatable!(1, 2)); 178 | static assert(!areEquatable!(1, "yo")); 179 | static assert(!areEquatable!(int, "yo")); 180 | static assert( areEquatable!(int, 1)); 181 | } 182 | 183 | /** 184 | Returns true of T can be check with null using an if statement 185 | */ 186 | template isNullTestable(T...) if (T.length == 1) { 187 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 188 | enum isNullTestable = __traits(compiles, { if (U.init is null) {} }); 189 | } 190 | 191 | /// 192 | unittest { 193 | class C {} 194 | struct S1 { 195 | void opAssign(int*) {} 196 | } 197 | static assert(!isNullTestable!S1); 198 | static assert( isNullTestable!C); 199 | static assert( isNullTestable!(int*)); 200 | 201 | struct S2 {} 202 | static assert(!isNullTestable!S2); 203 | static assert(!isNullTestable!int); 204 | 205 | class C2 { 206 | @disable this(); 207 | } 208 | static assert(isNullTestable!C2); 209 | } 210 | 211 | deprecated("use isNullSettable instead") 212 | template isNullable(T...) if (T.length == 1) { 213 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 214 | enum isNullable = __traits(compiles, { U u = U.init; u = null; }); 215 | } 216 | 217 | /** 218 | Returns true of T can be set to null 219 | */ 220 | template isNullSettable(T...) if (T.length == 1) { 221 | alias U = from.bolts.traits.symbols.TypesOf!T[0]; 222 | enum isNullSettable = __traits(compiles, { U u = U.init; u = null; }); 223 | } 224 | 225 | /// 226 | unittest { 227 | class C {} 228 | struct S1 { 229 | void opAssign(int*) {} 230 | } 231 | static assert(isNullSettable!S1); 232 | static assert(isNullSettable!C); 233 | static assert(isNullSettable!(int*)); 234 | 235 | struct S2 {} 236 | static assert(!isNullSettable!S2); 237 | static assert(!isNullSettable!int); 238 | 239 | struct S3 { 240 | @disable this(); 241 | void opAssign(int*) {} 242 | } 243 | static assert(isNullSettable!S3); 244 | } 245 | 246 | /** 247 | Returns a stringof another template with it's real and non-mangled types 248 | 249 | You may also customize the format in the case of templates: 250 | 251 | --- 252 | struct S(T, U) {} 253 | StringOf!(S!(int, int)).writeln; // "S!(int, int)" 254 | StringOf!(S!(int, int), "...", "<", ">").writeln; // "S" 255 | --- 256 | 257 | Params: 258 | T = the type you want to stringize 259 | sep = in case of a template, what's the deperator between types 260 | beg = in case of a template, what token marks the beginnig of the template arguments 261 | end = in case of a template, what token marks the end of the template arguments 262 | 263 | See_Also: 264 | - https://forum.dlang.org/post/iodgpllgtcefcncoghri@forum.dlang.org 265 | */ 266 | template StringOf(alias U, string sep = ", ", string beg = "!(", string end = ")") { 267 | import std.traits: TemplateOf; 268 | static if (__traits(compiles, TemplateOf!U) && !is(TemplateOf!U == void)) { 269 | import std.traits: TemplateArgsOf; 270 | import std.string: indexOf; 271 | import std.conv: text; 272 | import std.meta: AliasSeq, staticMap; 273 | alias Tmp = TemplateOf!U; 274 | alias Args = TemplateArgsOf!U; 275 | enum tmpFullName = Tmp.stringof; 276 | enum tmpName = tmpFullName[0..tmpFullName.indexOf('(')]; 277 | 278 | alias AddCommas(U...) = AliasSeq!(U, sep); 279 | alias ArgNames = staticMap!(.StringOf, Args); 280 | static if (ArgNames.length == 0) { 281 | alias SeparatedArgNames = AliasSeq!(); 282 | } else { 283 | alias SeparatedArgNames = staticMap!(AddCommas, ArgNames)[0 .. $-1]; 284 | } 285 | immutable StringOf = text(tmpName, beg, SeparatedArgNames, end); 286 | } else { 287 | static if (__traits(compiles, U.stringof)) { 288 | immutable StringOf = U.stringof; 289 | } else { 290 | immutable StringOf = typeof(U).stringof; 291 | } 292 | } 293 | } 294 | 295 | /// Ditto 296 | string StringOf(T, string sep = ", ", string beg = "!(", string end = ")")() if (is(TemplateOf!T == void)) { 297 | return T.stringof; 298 | } 299 | 300 | /// 301 | unittest { 302 | template A(T...) {} 303 | struct B {} 304 | struct C {} 305 | alias T = A!(A!(B, C)); 306 | assert(StringOf!T == "A!(A!(B, C))"); 307 | 308 | struct S(T) {} 309 | assert(StringOf!(S!int) == "S!(int)"); 310 | assert(StringOf!(A!(A!(B, S!int))) == "A!(A!(B, S!(int)))"); 311 | 312 | assert(StringOf!int == "int"); 313 | assert(StringOf!3 == "3"); 314 | 315 | void f(int a, int b) {} 316 | import std.algorithm: canFind; 317 | assert(StringOf!(f).canFind("void(int a, int b)")); 318 | 319 | assert(StringOf!void == "void"); 320 | } 321 | 322 | unittest { 323 | import std.meta: AliasSeq; 324 | import bolts.meta: AliasPack; 325 | alias T = AliasPack!(AliasSeq!()); 326 | assert(StringOf!T == "AliasPack!()"); 327 | } 328 | 329 | --------------------------------------------------------------------------------