Hi Quinn. I have to come back to this. I tried to implement a Python extension module, and got stuck at the point where the os_log...() functions require that the format string must be a C string literal. Of course, when you wrapper such a function into another programming language such as Python, the format string comes in as a string object, and that cannot become a C string literal.
The code snippet from the extension module is (simplified):
static PyObject *macos_oslog_os_log_error(PyObject *self, PyObject *args) {
const char * py_format = NULL;
if (!PyArg_ParseTuple(args, "s", &py_format)) {
return NULL;
}
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, py_format);
Py_RETURN_NONE;
}
The compile errors are (the first one seems to be the relevant one):
src/macos_oslog.c:111:5: error: static_assert failed due to requirement '__builtin_constant_p(py_format)' "format/label/description argument must be a string constant"
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, py_format);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/log.h:181:9: note: expanded from macro 'os_log_with_type'
OS_LOG_CALL_WITH_FORMAT(_os_log_impl, \
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:87:28: note: expanded from macro 'OS_LOG_CALL_WITH_FORMAT'
OS_LOG_PRAGMA_PUSH OS_LOG_STRING(LOG, _os_fmt_str, fmt); \
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:78:9: note: expanded from macro 'OS_LOG_STRING'
_Static_assert(__builtin_constant_p(_str), \
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
src/macos_oslog.c:111:5: error: array initializer must be an initializer list or string literal
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/log.h:181:9: note: expanded from macro 'os_log_with_type'
OS_LOG_CALL_WITH_FORMAT(_os_log_impl, \
^
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:87:47: note: expanded from macro 'OS_LOG_CALL_WITH_FORMAT'
OS_LOG_PRAGMA_PUSH OS_LOG_STRING(LOG, _os_fmt_str, fmt); \
^
src/macos_oslog.c:111:57: error: os_log() format argument is not a string constant
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, py_format);
^~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/log.h:182:55: note: expanded from macro 'os_log_with_type'
(&__dso_handle, _log_tmp, _type_tmp), format, ##__VA_ARGS__); \
^~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:88:99: note: expanded from macro 'OS_LOG_CALL_WITH_FORMAT'
uint8_t _Alignas(16) OS_LOG_UNINITIALIZED _os_fmt_buf[__builtin_os_log_format_buffer_size(fmt, ##__VA_ARGS__)]; \
^~~
The relevant code in os/trace_base.h is:
#define OS_LOG_STRING(_ns, _var, _str) \
_Static_assert(__builtin_constant_p(_str), \
"format/label/description argument must be a string constant"); \
__attribute__((section("__TEXT,__oslogstring,cstring_literals"),internal_linkage)) \
static const char _var[] __asm(OS_STRINGIFY(OS_CONCAT(LOS_##_ns, __COUNTER__))) = _str
Because it uses array initialization from a string, the string must be a string literal and cannot be a char* or any const variation of it. So the assertion is understandable, but not understandable is the array initialization that causes this restriction.
Update:
If I change the format string pointer to const char * const py_format, then the assertion passes, but there are still other errors:
src/macos_oslog.c:111:5: error: array initializer must be an initializer list or string literal
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, py_format);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/log.h:181:9: note: expanded from macro 'os_log_with_type'
OS_LOG_CALL_WITH_FORMAT(_os_log_impl, \
^
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:87:47: note: expanded from macro 'OS_LOG_CALL_WITH_FORMAT'
OS_LOG_PRAGMA_PUSH OS_LOG_STRING(LOG, _os_fmt_str, fmt); \
^
src/macos_oslog.c:111:57: error: os_log() format argument is not a string constant
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, py_format);
^~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/log.h:182:55: note: expanded from macro 'os_log_with_type'
(&__dso_handle, _log_tmp, _type_tmp), format, ##__VA_ARGS__); \
^~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/os/trace_base.h:88:99: note: expanded from macro 'OS_LOG_CALL_WITH_FORMAT'
uint8_t _Alignas(16) OS_LOG_UNINITIALIZED _os_fmt_buf[__builtin_os_log_format_buffer_size(fmt, ##__VA_ARGS__)]; \
^~~
This time, the __builtin_os_log_format_buffer_size() macro determines the log buffer size at compile time, which again does not work with a string object whose size is known only at run time.
These implementation choices basically prevent implementing a Python wrapper. I'm not an expert in bindings to other languages, but I suppose it would run into the same issues.
Any advice on how to solve this issue?
If you agree that there is no way around this, could you please take that to the dev team for considering an approach that supports integrating this into other programming languages?