├── .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 | [](https://code.dlang.org/packages/bolts) [](https://travis-ci.org/aliak00/bolts) [](https://codecov.io/gh/aliak00/bolts) [](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 |
--------------------------------------------------------------------------------