.. _DuneInstance:

DuneInstance
============

.. cmake_module::

   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.)

   .. rubric:: 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: :ref:`dune_instance_begin() <dune_instance_begin>`,
   :ref:`dune_instance_add() <dune_instance_add>`, and
   :ref:`dune_instance_end() <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 :ref:`dune_instance_begin() <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 :ref:`dune_instance_add() <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.

   :ref:`dune_instance_end() <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.

   .. rubric:: Embedded Templates

   The template files given in :ref:`dune_instance_begin()
   <dune_instance_begin>` can contain embedded templates.  These will be
   instantiated by :ref:`dune_instance_add() <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.

   .. rubric:: 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 :ref:`dune_instance_add()
   <dune_instance_add>` or :ref:`dune_instance_begin()
   <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.

   .. rubric:: Main Interface

   These are the ones you normally use.

   * :ref:`dune_instance_begin() <dune_instance_begin>`
   * :ref:`dune_instance_add() <dune_instance_add>`
   * :ref:`dune_instance_end() <dune_instance_end>`
   * :ref:`DUNE_INSTANCE_GENERATED <DUNE_INSTANCE_GENERATED>`

   .. rubric:: Utilities

   You would not use these directly under normal circumstances.

   * :ref:`dune_instance_parse_file_spec() <dune_instance_parse_file_spec>`
   * :ref:`dune_instance_from_id() <dune_instance_from_id>`
   * :ref:`dune_instance_generate_file() <dune_instance_generate_file>`


