Description
When a C++ template uses sizeof(T) in its default arguments, the corresponding Nim binding cannot use that default because Nim's sizeof on opaque importcpp template parameters requires .completeStruct, which is semantically incompatible with opaque types.
Minimal Repro
{.emit: """/*TYPESECTION*/
template<typename T, int N = 1024 / sizeof(T) + 8>
struct Buf {
T data[N];
};
""".}
type
# Naive binding emits the default as-is:
Buf*[T; N: static cint = 1024 div sizeof(T) + 8] {.importcpp: "Buf<'0, '1>".} = object
proc main() =
var x: Buf[cint] # cannot use C++-side default
main()
Result:
Error: 'sizeof' requires '.importc' types to be '.completeStruct'
If we strip the default to side-step the error:
type
Buf*[T; N: static cint] {.importcpp: "Buf<'0, '1>".} = object
proc main() =
var x: Buf[cint] # Error: cannot instantiate Buf, got: <typedesc[cint]>, but expected: <T, N>
So the C++-side default is effectively dead either way.
Impact
sizeof(T) in template defaults is a common pattern in C++ libraries (containers, buffers, etc.). When writing Nim bindings, these defaults cannot be honored:
- The user is forced to specify the second template parameter every time (
Buf[float, 1024] instead of Buf[float]).
- The "use it like the C++ documentation says" experience breaks.
- It is not possible to express the default in any equivalent way on the Nim side, so this becomes a hard expressivity gap of
importcpp template defaults.
It would be useful if either:
- Nim allowed
sizeof on opaque importcpp template params for template-default purposes (the value is resolved at C++ instantiation time anyway), or
- Nim allowed declaring a generic param without a default such that omission falls through to the underlying C++ template default during importcpp instantiation.
Related
Description
When a C++ template uses
sizeof(T)in its default arguments, the corresponding Nim binding cannot use that default because Nim'ssizeofon opaqueimportcpptemplate parameters requires.completeStruct, which is semantically incompatible with opaque types.Minimal Repro
{.emit: """/*TYPESECTION*/ template<typename T, int N = 1024 / sizeof(T) + 8> struct Buf { T data[N]; }; """.} type # Naive binding emits the default as-is: Buf*[T; N: static cint = 1024 div sizeof(T) + 8] {.importcpp: "Buf<'0, '1>".} = object proc main() = var x: Buf[cint] # cannot use C++-side default main()Result:
If we strip the default to side-step the error:
So the C++-side default is effectively dead either way.
Impact
sizeof(T)in template defaults is a common pattern in C++ libraries (containers, buffers, etc.). When writing Nim bindings, these defaults cannot be honored:Buf[float, 1024]instead ofBuf[float]).importcpptemplate defaults.It would be useful if either:
sizeofon opaque importcpp template params for template-default purposes (the value is resolved at C++ instantiation time anyway), orRelated
[C++]codegen issue cluster: [C++] =wasMoved + {.importcpp: #.foo().} does not replace # #25800 (=wasMoved), [C++] default(T) initializer list may lead to ambiguous assignment and prevent compilation #25803 (default(T)).