If this is the only way to go, I guess it should be something at one level of abstraction higher, to pass the macro, the flag, and the expression (so that it will work with @async but also with @time and others).
And again, I guess one of the issues will be that macros can’t be passed around (referenced) as arguments (or at least I don’t know how).
Sorry, I don’t think I properly articulated what was going through my head.
I have these global (environment) variables which are set upon application initialisation and don’t change throughout the lifetime of the script. Things like DEBUG = true | false, ENV = dev | test | prod, etc. These affect whether or not subsequent macros like @time, @async, etc are used (ie if ENV == dev then many statements are prefixed by the @time macro; but in prod they are not).
Oh, yes, that’s a good point. They’re not actually constants but fields in a config object. I could se tup some key constants, like const DEBUG = config.debug. Or maybe just refactor the config type to be immutable - that should have a similar effect as the constants, I expect.
I was actually recently trying to figure out this exact case of making @threads optional, based on a flag passed to a function. Would the macro you’re proposing be applicable for something like the following?
function func(args...; use_threads=false)
@maybethread use_threads for i = 1:10
If not, is there a macro that would work for this kind of use case?
I suppose one fundamental distinction is whether you want to make the threads optional at runtime vs at compile time. I think you (and the OP) were primarily looking for a compile-time switch, whereas I’m looking for a runtime switch.
Based on @jlapeyre’s answer, it occurred to me that maybe the easiest way to do this for @threads is to just modify the definition of the macro from the standard library. Indeed, the following seems like it works perfectly for me:
using Base.Threads: threadid, threading_run
macro threadsif(cond, loop)
if !(isa(loop, Expr) && loop.head === :for)
throw(ArgumentError("@threadsif requires a `for` loop expression"))
if !(loop.args isa Expr && loop.args.head === :(=))
throw(ArgumentError("nested outer loops are not currently supported by @threadsif"))
$(Threads._threadsfor(loop.args, loop.args, :static))
I’ve left out the schedule from the original implementation for conciseness since that’s currently limited to :static anyway. It wouldn’t be hard to add that back in.
Edit (usage example):
I currently have the above macro definition “in production” at QuantumControlBase.jl, and it is used e.g. here:
version 2 of my maybethread macro above, which optionally threads at compile time, is now hygienic:
quote Threads.@threads $(Expr(loop.head,
# @warn "running single threaded"
quote $(esc(loop)); end
i typically define use_threads = Threads.nthreads()>1.