{"action":"created","issue":{"url":"https://api.github.com/repos/andrewrk/zig/issues/11","labels_url":"https://api.github.com/repos/andrewrk/zig/issues/11/labels{/name}","comments_url":"https://api.github.com/repos/andrewrk/zig/issues/11/comments","events_url":"https://api.github.com/repos/andrewrk/zig/issues/11/events","html_url":"https://github.com/andrewrk/zig/issues/11","id":121688096,"number":11,"title":"add conditional compilation and ability to check target platform and architecture","user":{"login":"andrewrk","id":106511,"avatar_url":"https://avatars.githubusercontent.com/u/106511?v=3","gravatar_id":"","url":"https://api.github.com/users/andrewrk","html_url":"https://github.com/andrewrk","followers_url":"https://api.github.com/users/andrewrk/followers","following_url":"https://api.github.com/users/andrewrk/following{/other_user}","gists_url":"https://api.github.com/users/andrewrk/gists{/gist_id}","starred_url":"https://api.github.com/users/andrewrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrewrk/subscriptions","organizations_url":"https://api.github.com/users/andrewrk/orgs","repos_url":"https://api.github.com/users/andrewrk/repos","events_url":"https://api.github.com/users/andrewrk/events{/privacy}","received_events_url":"https://api.github.com/users/andrewrk/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/andrewrk/zig/labels/enhancement","name":"enhancement","color":"84b6eb"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-12-11T11:11:15Z","updated_at":"2016-01-12T11:42:00Z","closed_at":null,"body":""},"comment":{"url":"https://api.github.com/repos/andrewrk/zig/issues/comments/170885123","html_url":"https://github.com/andrewrk/zig/issues/11#issuecomment-170885123","issue_url":"https://api.github.com/repos/andrewrk/zig/issues/11","id":170885123,"user":{"login":"andrewrk","id":106511,"avatar_url":"https://avatars.githubusercontent.com/u/106511?v=3","gravatar_id":"","url":"https://api.github.com/users/andrewrk","html_url":"https://github.com/andrewrk","followers_url":"https://api.github.com/users/andrewrk/followers","following_url":"https://api.github.com/users/andrewrk/following{/other_user}","gists_url":"https://api.github.com/users/andrewrk/gists{/gist_id}","starred_url":"https://api.github.com/users/andrewrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrewrk/subscriptions","organizations_url":"https://api.github.com/users/andrewrk/orgs","repos_url":"https://api.github.com/users/andrewrk/repos","events_url":"https://api.github.com/users/andrewrk/events{/privacy}","received_events_url":"https://api.github.com/users/andrewrk/received_events","type":"User","site_admin":false},"created_at":"2016-01-12T11:41:59Z","updated_at":"2016-01-12T11:41:59Z","body":"When an if statement is analyzed, the condition is checked for constant expression evaluation. If the condition is available at compile time, then this if statement becomes a conditional compilation construct. Combine this with a compiler function which checks compile information, and we're already almost done:\r\n\r\n```zig\r\nfn f() {\r\n    if (#compile_var(\"os\") == \"linux\") {\r\n        // ...\r\n    } else {\r\n        // ...\r\n    }\r\n}\r\n```\r\n\r\n`#compile_var()` can look up a string in a table of variables defined at compile time. A compile variable might be any type.\r\n\r\nUsually, if an if condition's \"then\" expression or \"else\" expression is unreachable because the condition is constant, it will generate an \"unreachable code\" error. However, we can annotate the constant expression with a value indicating that it can differ depending on compile time variables. When this annotation is present, the \"unreachable code\" error is not generated.\r\n\r\nSo, conditional compilation is perfectly integrated into the language's syntax within a function. Let's look at outside a function:\r\n\r\n```zig\r\nconst foo : i32 = if (#compile_var(\"release\")) 1234 else 5678;\r\n```\r\n\r\nGlobal constants and variables can use full expression syntax for their initializers, so this works here too.\r\n\r\nHow about a global initialized list of things ([example](https://github.com/andrewrk/libsoundio/blob/6b8c306ea182ec89147ce9ae4e71f804f5777641/src/soundio.c#L17)), of which the length of the list is dependent on compile time variables?\r\n\r\nIn C we have:\r\n\r\n```c\r\nstatic const enum SoundIoBackend available_backends[] = {\r\n#ifdef SOUNDIO_HAVE_JACK\r\n    SoundIoBackendJack,\r\n#endif\r\n#ifdef SOUNDIO_HAVE_PULSEAUDIO\r\n    SoundIoBackendPulseAudio,\r\n#endif\r\n#ifdef SOUNDIO_HAVE_ALSA\r\n    SoundIoBackendAlsa,\r\n#endif\r\n#ifdef SOUNDIO_HAVE_COREAUDIO\r\n    SoundIoBackendCoreAudio,\r\n#endif\r\n#ifdef SOUNDIO_HAVE_WASAPI\r\n    SoundIoBackendWasapi,\r\n#endif\r\n    SoundIoBackendDummy,\r\n};\r\n```\r\n\r\nIn Zig we take advantage of `void` array elements being removed from the array:\r\n\r\n```zig\r\nconst available_backends = [\r\n    if (#compile_var(\"have_jack\")) SoundIoBackend.Jack else void,\r\n    if (#compile_var(\"have_pulse_audio\")) SoundIoBackend.PulseAudio else void,\r\n    if (#compile_var(\"have_alsa\")) SoundIoBackend.Alsa else void,\r\n    if (#compile_var(\"have_core_audio\")) SoundIoBackend.CoreAudio else void,\r\n    if (#compile_var(\"have_wasapi\")) SoundIoBackend.Wasapi else void,\r\n    SoundIoBackend.Dummy,\r\n];\r\n```\r\n\r\nNote that technically the `else void` could be omitted since an if statement with no else defaults to `else void`. It may be that `void` is the wrong type for this, but this construct is powerful and so if `void` is the wrong type then we may introduce a \"deleted array item\" type.\r\n\r\nNext problem. Anything that you can export, you might want to *not* export it. So, for any top level declaration for which `export` is valid - this currently includes functions, global variables, enums, and structs, we will also define a directive, let's call it `#condition()` which accepts a boolean expression which decides if the top level declaration is included in the compilation. If the top level declaration is not included, then calling it or otherwise depending upon it is a compile time error. Example:\r\n\r\n```zig\r\n#condition(#compile_var(\"os\") == \"win32\")\r\nexport fn trace_win32() {\r\n     const err = get_last_win32_error();\r\n     print_win32_error(err);\r\n}\r\n\r\nfn f1() {\r\n    if (#compile_var(\"os\") == \"win32\") {\r\n        trace_win32(); // OK, since const expression eval ensured this didn't actually run when trace_win32 is not exported\r\n    }\r\n    trace_win32(); // error when building with compile var \"os\" == \"win32\": trace_win32 symbol not defined\r\n}\r\n```\r\n\r\nFinally the only problem left is the syntax that we use to import C .h files. We don't yet have a syntax for directly importing a C .h file, but it's planned. We would need to conditionally import some .h files and not others depending on compile variables. I'll defer figuring out how that will work until we actually have that construct. But I will mention that it's probably planned that the best use of the auto C .h file importing feature is to limit it to a single invocation that would declare the order in which preprocessor defines are defined and what their values are as well as the order of (virtual) `#include`s. So, one could associate preprocessor defines with expressions and `#include`s with boolean expressions."}}