DuneInstance¶
This module can be used to generate explicit template instantiations. Suppose you have a template test function that you want to call for a number of template arguments. You want to explicitly instantiate the function for each set of template arguments, and put the instanciation into its own translation unit. (This can be beneficial in that it limits the amount of code that the optimizer sees at once, and thus it can reduce both memory and cpu requirements during compilation.)
Examples
Let’s say you are writing a test mytest.cc
and need to call a
template function for several types:
#include <mytestsuite.hh>
int main() {
MyTestSuite suite;
suite.test<bool>();
suite.test<char>();
suite.test<int>();
suite.test<double>();
return suite.good() ? EXIT_SUCCESS : EXIT_FAILURE;
}
Let’s further say that you want to explicitly instantiate each used
MyTestSuite::test()
instance in it’s own translation unit. Then you
need a series of files mytest_instance_bool.cc
,
mytest_instance_char.cc
, etc, all with essentially the same content:
#include <mytestsuite.hh>
template void MyTestSuite::test<@TYPE@>();
where @TYPE@
is replaced by bool
, char
, etc as appropriate.
This is however not enough: all translation units need to know which
instances of MyTestSuite::test()
are instantiated explicitly so they
do not instantiate them implicitly themselves (that would violate the
one-definition rule). C++ only allows to declare individual instances as
extern, not all of them collectively, so we need to put a list of all
these instances into a header mytest.hh
:
#include <mytestsuite.hh>
extern template void MyTestSuite::test<bool>();
extern template void MyTestSuite::test<char>();
extern template void MyTestSuite::test<int>();
extern template void MyTestSuite::test<double>();
We also need to include that header from each translation unit in the
test, we can simply replace #include <mytestsuite.hh>
with #include
<mytest.hh>
.
This is of course tedious and prone tp break if the list of tested types changes. To make this less fragile this module provides a series of commands: dune_instance_begin(), dune_instance_add(), and dune_instance_end(), which can be used to automatically generate the explicit instantiations for each type and the contents for the header and the body of main.
This may look like this in CMakeLists.txt
:
dune_instance_begin(FILES mytest.cc mytest.hh)
foreach(TYPE IN ITEMS bool char int double)
dune_instance_add(ID "${TYPE}" FILES mytest_instance.cc)
endforeach(TYPE IN ITEMS bool char int double)
dune_instance_end()
dune_list_filter(DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]])
dune_add_test(NAME mytest
SOURCES ${DUNE_INSTANCE_GENERATED})
The call to dune_instance_begin() reads
mytest.cc.in
and mytest.hh.in
and splits them into embedded
templates and other content. It will replace occurrances of @VAR@
now in the other content and save the result for later.
The call to dune_instance_add() occurs in a
loop. Each call will instanciate the embedded templates extracted
earlier to replace occurance of @TYPE@
by the value of the variable
TYPE
set in the for loop. Then files containing explicit
instantiatons will be generated as mytest_instance_bool.cc
,
mytest_instance_bool.cc
, etc, from a template file
mytest_instance.cc.in
. The name of the generated files are the base
file name from the template definition with the ID
inserted before
the extension. The name of the template file is the same base file name
with .in
appended.
dune_instance_end() is used to write
mytest.cc
and mytest.hh
with the collected content from the
embedded templates. The list of generated files will be available in the
variable DUNE_INSTANCE_GENERATED
.
The template files then look like this:
mytest.cc.in
:
// @GENERATED_SOURCE@
#include <config.h>
#include <mytest.hh>
int main() {
MyTestSuite suite;
#cmake @template@
suite.test<@TYPE@>();
#cmake @endtemplate@
return suite.good() ? EXIT_SUCCESS : EXIT_FAILURE;
}
mytest.hh.in
:
// @GENERATED_SOURCE@
#include <mytestsuite.hh>
#cmake @template@
extern template void MyTestSuite::test<@TYPE@>();
#cmake @endtemplate@
mytest_instance.cc.in
:
// @GENERATED_SOURCE@
#include <config.h>
#include <mytest.hh>
template void MyTestSuite::test<@TYPE@>();
The @GENERATED_SOURCE@
substitution is good practice, it tells a
human reader that this file was generated and what the template file was,
and it hints editors to go into read-only mode.
Embedded Templates
The template files given in dune_instance_begin() can contain embedded templates. These will be instantiated by dune_instance_add(), and all instantiations will be concatenated together and replace the original embedded template.
The begin of an embedded template is marked by a line containing
@template@
or @template NAME@
. Leaving off the name is
equivalent to an empty name. dune_instance_add(TEMPLATE NAME)
will
only instanciate embedded templates whose name matches and ignore all
others.
The end of an embedded template is marked by a line containing
@endtemplate@
or @endtemplate NAME@
. If a name is given, it must
match the name of the embedded template it closes. If no name is given
(or the name is empty), that check is omitted.
There may be arbitrary characters on the same line before or after the
begin and end markers. These are ignored, so you can use them for
comments or to trick your editor into proper indentation. The one
exception is that the line surrounding the marker may not contain any
@
characters to avoid ambiguities.
How Files And Strings Are Generated
The generation is done using the cmake command configure_file(...)
for template files and string(CONFIGURE ...)
for template strings.
These simply substitute the current variable values, so make sure to set
up the variables to substitute before calling dune_instance_add() or dune_instance_begin().
Refrain from using substitutions that begin with an underscore
(i.e. @_my_local_var@
). The generation functions in this module use
such names for their local variables and may hide the variable you are
trying to substitude.
When instantiating files we set up a few convenience variables before
calling configure_file()
that can be used in substitutions:
@TEMPLATE@
contains the name of the template file. @INSTANCE@
contains the name of the file being generated, not including an implied
${CMAKE_CURRENT_BINARY_DIR}
. Use @BINDIR_INSTANCE@
if you do
want the implied ${CMAKE_CURRENT_BINARY_DIR}
. @GENERATED_SOURCE@
contains a one-line message that this file was generated, including the
name of the template file.
Main Interface
These are the ones you normally use.
Utilities
You would not use these directly under normal circumstances.