#comptime conditional existence of variables
1 messages · Page 1 of 1 (latest)
var foo: if (want_foo) u32 else void = undefined;
not as nice conceptually, but that would have the same effect, right?
If you want it to actually not exist you can use rs usingnamespace if (want_foo) struct { var foo: int = undefined; } else struct {};
But that means you need to access it via namespace
Alternatively you can just define it unconditionally and never use it :)
also, sorry to ask, but I'm slightly confused on how to make a variable comptime in the first place, would this work?
var want_foo: bool = comptime if(builtin.mode == .Debug) true else false;
As in you want the variable to be mutable at comptime or you want it to have an initial value defined at comptime?
I want the variable to exist only at comptime
as through it were a comptime function argument
Well, a comptime argument is a const, not a var
So const want_foo = builtin.mode == .Debug;
(it's comptime-only because its value is comptime-known)
well, then the user can't modify it which would kinda defeat the point :/ thanks though
If you want it to be mutable at comptime, comptime var want_foo = builtin.mode == Debug;
hmm that was giving me an error with zls earlier
What error exactly?
Ah, it can't be a global
...
global mutability is forbidden at comptime because it would make execution order observable
How am I supposed to go about doing this? I just want a user-modifiable comptime variable that detemines whether foo is available
Does std.meta.globalOption do what you want?
If you use std.meta.globalOption("foo", bool) in your code, it allows a user to define pub const foo = true; in their main file
Which will then be returned from that globalOption call
If no such option is defined it'll return null so you can use orelse to set a default
(or throw a compile error if it's required)
It's a bit weird coming from other languages but it's a common pattern in zig :)
eg. it's used in std to set the IO mode
why go through all this effort just to be able to sue the common want_thing idiom :/
but fair, thank you for the help!
Do you have any suggestions for a better alternative?
I guess allowing this -- not quite sure what you mean by making execution order observable, that's the whole point right
You can't allow that and still have reproducible builds
Because if you have two comptime blocks they could execute in any order
And you can't define an order for it without severely limiting parallelism opportunities
oh, I think I get what you mean, thanks
(in fact, allowing global mutability at all would already limit parallelism)
(but tbf we're already doing single-TU builds, we don't seem to care about parallelism )
single-TU doesn't mean single threaded
sure but that very very heavily limits parallelism
Not at all
Maybe it does within LLVM, but the zig frontend is very much geared for parallelism
if I have like 5 executables I want to build with lots of shared code and i build them with build-exe, it will take a ton of time instead of compiling a single source file and linking with previous objects, but this is kind of unavoidable with zig itself so
idk
just different programming paradigms ig
And if LLVM is limited there (I'm not sure, haven't tested), it's probably because it's geared to building C and C++, where multiple-TU builds are extremely common so there's no point in multithreading it
Not because of some inherent serial property of single-TU builds :)
I mean, single-TU builds /are/ inherently more serial than multi-TU builds if you have lots of shared code and multiple executables to build, like in many large projects
but it's unfortunate that this problem isn't really fixable without severely limiting the capability of the language
Not really, you can easily parallelize the optimization of independent functions
but this is a different beast right
And multi-TU builds still have a serial LTO step
(if they even do LTO, which many don't)
that is true
it's still not as bad as single-TU builds because of shared libraries though
Ah sorry I missed the bit about multiple executables - Zig will eventually cache most (all?) of the frontend stuff there, and possibly some of the backend stuff, not sure
So it should be about as fast as multi-TU builds, if not faster
this is probably just an issue with me using makefiles instead of zig build
Also once selfhosted backends are done and binary patching lands in master, Zig's debug build speed will be way faster than any other compiler in existence, multi-TU or not :)
Everything goes through the GOT
So you can just add more functions on the end and patch the GOT
smart
At the cost of a tiny bit of runtime perf (which we don't care about anyway because yknow, debug build)
by the way, can I turn off the status output specifically without turning off errors? this seems to screw up my output a bit
that would just redirect stderr to stdout
and these messages are printed to stderr as well as errors
oh, I get what you mean
it uses isatty
Yeah
meh, thanks
Might be worth an issue :)
unfortunately I'm going through some issues with github rn so that probably won't be me
And, sorry to have so many, but another question, is there a line-buffered BufferedWriter?
Hey, I hate to ask again, but now I have a struct using that optional variable with usingnamespace and functions using that conditionally:
usingnamespace if (getWantChecking()) struct {
var initialized: bool = false;
} else struct {};
pub fn init(allocator: std.mem.Allocator) void {
if(comptime getWantChecking())
initialized = true;
a = allocator;
}
but the compiler complains about initialized being undeclared, even though it's only ever used when it's actually declared. this is probably a misunderstanding on my part, could you help me fix it? (also please don't complain about the globals, it's for a C interface)
usingnamespace doesn't modify the current namespace, so you have to access it externally
You can use @This().initialized, or just put it in a struct instead of using usingnamespace
wait, wot
But honestly there's nothing wrong with just having the variable exist always; if it's never used it won't end up in the binary
I do prefer the nicest approach though :)
in any case, thank you, I'll just put it in its own struct...
I mean, at this point I would just do something like var initialised: bool = if (getWantChecking()) false else @compileError("Don't want checking");. You'd have all of the same conditional branches as you would with the conditional namespace approach, but without all the extra boilerplate in the declaration
huh? how does that help here?
I don't want the build to fail whenever the user doesn't request checking
That's actually a better solution yeah
or am I missing something here
It'll only fail if you reference the variable
that's such a hack
it's not a hack though, that's just a way things are done in zig. When a function is deprecated, it's replaced with const name = @compileError("DEPRECATED: ...");
in this situation, you functionally get the same behaviour, in that you get a compilation error if you try to reference it when it's been disabled, just without all the extra code using the namespace solution
k
I think it would be interesting to consider why you think this, only, since
ifs with comptime conditions only compile the true branch, setting a constant and then branching on it is quite simple and achieves what you want.
[This is how Zig achieves conditional compilation.]- File-scope declarations are only compiled if they are referenced.
[This is also part of how Zig achieves conditional compilation.] - The
usingnamespacetrick is quite a bit harder to read and understand.
[More on this below.]
It may seem like there's a lot of moving parts here that are being used to make this mechanism work -- and that's kinda true, there's several moving parts.
The point though is that Zig tries to achieve everything with as little as possible, rather than adding thousands of little features to cover everything that's wanted.
As a result, these 'moving parts' are ubiquitous, and therefore familiar when reading other people's code.
// neither are present in binary if not referenced, and former is a constant
// so can be inlined into its usage sites trivially.
const want_check = std.meta.globalOption("want_check") orelse false;
var check_data = ...;
fn foo(input: u32) !u32 {
// compiled away when 'want_check' is false
if (want_check) {
try check_data.validate(input);
}
return input * 2 + 1; // do something normal here
}
In this way, this reads very similarly to #defines as used in C, and isn't that hard to understand; you're merely stating where the info comes from, and then branching on it.
The fact that there's 'moving parts' here is essentially secondary.