Skip to content

Commit 2fcbdb0

Browse files
committed
[docs] [C++20] [Modules] Ideas for transiting to modules
1 parent 9dd40f8 commit 2fcbdb0

File tree

1 file changed

+339
-0
lines changed

1 file changed

+339
-0
lines changed

clang/docs/StandardCPlusPlusModules.rst

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,345 @@ the following style significantly:
610610

611611
The key part of the tip is to reduce the duplications from the text includes.
612612

613+
Ideas for converting to modules
614+
-------------------------------
615+
616+
For new libraries, we encourage them to use modules completely from day one if possible.
617+
This will be pretty helpful to make the whole ecosystems to get ready.
618+
619+
For many existing libraries, it may be a breaking change to refactor themselves
620+
into modules completely. So that many existing libraries need to provide headers and module
621+
interfaces for a while to not break existing users.
622+
Here we provide some ideas to ease the transition process for existing libraries.
623+
**Note that the this section is only about helping ideas instead of requirement from clang**.
624+
625+
Let's start with the case that there is no dependency or no dependent libraries providing
626+
modules for your library.
627+
628+
ABI non-breaking styles
629+
~~~~~~~~~~~~~~~~~~~~~~~
630+
631+
export-using style
632+
^^^^^^^^^^^^^^^^^^
633+
634+
.. code-block:: c++
635+
636+
module;
637+
#include "header_1.h"
638+
#include "header_2.h"
639+
...
640+
#include "header_n.h"
641+
export module your_library;
642+
export namespace your_namespace {
643+
using decl_1;
644+
using decl_2;
645+
...
646+
using decl_n;
647+
}
648+
649+
As the example shows, you need to include all the headers containing declarations needs
650+
to be exported and `using` such declarations in an `export` block. Then, basically,
651+
we're done.
652+
653+
export extern-C++ style
654+
^^^^^^^^^^^^^^^^^^^^^^^
655+
656+
.. code-block:: c++
657+
658+
module;
659+
#include "third_party/A/headers.h"
660+
#include "third_party/B/headers.h"
661+
...
662+
#include "third_party/Z/headers.h"
663+
export module your_library;
664+
#define IN_MODULE_INTERFACE
665+
extern "C++" {
666+
#include "header_1.h"
667+
#include "header_2.h"
668+
...
669+
#include "header_n.h"
670+
}
671+
672+
Then in your headers (from ``header_1.h`` to ``header_n.h``), you need to define the macro:
673+
674+
.. code-block:: c++
675+
676+
#ifdef IN_MODULE_INTERFACE
677+
#define EXPORT export
678+
#else
679+
#define EXPORT
680+
#endif
681+
682+
And you should put ``EXPORT`` to the beginning of the declarations you want to export.
683+
684+
Also it is suggested to refactor your headers to include thirdparty headers conditionally:
685+
686+
.. code-block:: c++
687+
688+
+ #ifndef IN_MODULE_INTERFACE
689+
#include "third_party/A/headers.h"
690+
+ #endif
691+
692+
#include "header_x.h"
693+
694+
...
695+
696+
This may be helpful to get better diagnostic messages if you forgot to update your module
697+
interface unit file during maintaining.
698+
699+
The reasoning for the practice is that the declarations in the language linkage are considered
700+
to be attached to the global module. So the ABI of your library in the modular version
701+
wouldn't change.
702+
703+
While this style looks not as convenient as the export-using style, it is easier to convert
704+
to other styles.
705+
706+
ABI breaking style
707+
~~~~~~~~~~~~~~~~~~
708+
709+
The term ``ABI breaking`` sounds terrifying generally. But you may want it here if you want
710+
to force your users to introduce your library in a consistent way. E.g., they either include
711+
your headers all the way or import your modules all the way.
712+
The style prevents the users to include your headers and import your modules at the same time
713+
in the same repo.
714+
715+
The pattern for ABI breaking style is similar with export extern-C++ style.
716+
717+
.. code-block:: c++
718+
719+
module;
720+
#include "third_party/A/headers.h"
721+
#include "third_party/B/headers.h"
722+
...
723+
#include "third_party/Z/headers.h"
724+
export module your_library;
725+
#define IN_MODULE_INTERFACE
726+
#include "header_1.h"
727+
#include "header_2.h"
728+
...
729+
#include "header_n.h"
730+
731+
#if the number of .cpp files in your project are small
732+
module :private;
733+
#include "source_1.cpp"
734+
#include "source_2.cpp"
735+
...
736+
#include "source_n.cpp"
737+
#else // the number of .cpp files in your project are a lot
738+
// Using all the declarations from thirdparty libraries which are
739+
// used in the .cpp files.
740+
namespace third_party_namespace {
741+
using third_party_decl_used_in_cpp_1;
742+
using third_party_decl_used_in_cpp_2;
743+
...
744+
using third_party_decl_used_in_cpp_n;
745+
}
746+
#endif
747+
748+
(And add `EXPORT` and conditional include to the headers as suggested in the export
749+
extern-C++ style section)
750+
751+
Remember that the ABI get changed and we need to compile our source files into the
752+
new ABI format. This is the job of the additional part of the interface unit:
753+
754+
.. code-block:: c++
755+
756+
#if the number of .cpp files in your project are small
757+
module :private;
758+
#include "source_1.cpp"
759+
#include "source_2.cpp"
760+
...
761+
#include "source_n.cpp"
762+
#else // the number of .cpp files in your project are a lot
763+
// Using all the declarations from thirdparty libraries which are
764+
// used in the .cpp files.
765+
namespace third_party_namespace {
766+
using third_party_decl_used_in_cpp_1;
767+
using third_party_decl_used_in_cpp_2;
768+
...
769+
using third_party_decl_used_in_cpp_n;
770+
}
771+
#endif
772+
773+
In case the number of your source files are small, we may put everything in the private
774+
module fragment directly. (it is suggested to add conditional include to the source
775+
files too). But it will make the compilation of the module interface unit to be slow
776+
when the number of the source files are not small enough.
777+
778+
**Note that the private module fragment can only be in the primary module interface unit
779+
and the primary module interface unit containing private module fragment should be the only
780+
module unit of the corresponding module.**
781+
782+
In that case, you need to convert your source files (.cpp files) to module implementation units:
783+
784+
.. code-block:: c++
785+
786+
+ #ifndef IN_MODULE_INTERFACE
787+
// List all the includes here.
788+
#include "third_party/A/headers.h"
789+
...
790+
#include "header.h"
791+
+ #endif
792+
793+
+ module your_library;
794+
795+
// Following off should be unchanged.
796+
...
797+
798+
The module implementation unit will import the primary module implicitly.
799+
We don't include any headers in the module implementation units
800+
here since we want to avoid duplicated declarations between translation units.
801+
This is the reason why we add non-exported using declarations from the third
802+
party libraries in the primary module interface unit.
803+
804+
And if you provide your library as ``libyour_library.so``, you probably need to
805+
provide a modular one ``libyour_library_modules.so`` since you changed the ABI.
806+
807+
What if there are headers only inclued by the source files
808+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
809+
810+
The above practice may be problematic if there are headers only included by the source
811+
files. If you're using private module fragment, you may solve the issue by including them
812+
in the private module fragment. While it is OK to solve it by including the implementation
813+
headers in the module purview if you're using implementation module units, it may be
814+
suboptimal since the primary module interface units now containing entities not belongs
815+
to the interface.
816+
817+
If you're a perfectionist, maybe you can improve it by introducing internal module partition unit.
818+
819+
The internal module partition unit is an importable module unit which is internal
820+
to the module itself. The concept just meets the headers only included by the source files.
821+
822+
We don't show code snippet since it may be too verbose or not good or not general.
823+
But it may not be too hard if you can understand the points of the section.
824+
825+
Providing a header to skip parsing redundant headers
826+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
827+
828+
It is a problem for clang to handle redeclarations between translation units.
829+
Also there is a long standing issue in clang (`problematic include after import <https://github.com/llvm/llvm-project/issues/61465>`_).
830+
But even if the issue get fixed in clang someday, the users may still get slower compilation speed
831+
and larger BMI size. So it is suggested to not include headers after importing the corresponding
832+
library.
833+
834+
However, it is not easy for users if your library are included by other dependencies.
835+
836+
So the users may have to write codes like:
837+
838+
.. code-block:: c++
839+
840+
#include "third_party/A.h" // #include "your_library/a_header.h"
841+
import your_library;
842+
843+
or
844+
845+
.. code-block:: c++
846+
847+
import your_library;
848+
#include "third_party/A.h" // #include "your_library/a_header.h"
849+
850+
For such cases, we suggest the libraries providing modules and the headers at the same time
851+
to provide a header to skip parsing all the headers in your libraries. So the users can
852+
import your library as the following style to skip redundant handling:
853+
854+
.. code-block:: c++
855+
856+
import your_library;
857+
#include "your_library_imported.h"
858+
#include "third_party/A.h" // #include "your_library/a_header.h" but got skipped
859+
860+
The implementation of ``your_library_imported.h`` can be a set of controlling macros or
861+
an overall controlling macro if you're using `#pragma once`. So you can convert your
862+
headers to:
863+
864+
.. code-block:: c++
865+
866+
#pragma once
867+
+ #ifndef YOUR_LIBRARY_IMPORTED
868+
...
869+
+ #endif
870+
871+
Importing modules
872+
~~~~~~~~~~~~~~~~~
873+
874+
When there are dependent libraries providing modules, we suggest you to import that in
875+
your module.
876+
877+
Most of the existing libraries would fall into this catagory once the std module gets available.
878+
879+
All dependent libraries providing modules
880+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
881+
882+
Life gets easier if all the dependent libraries providing modules.
883+
884+
You need to convert your headers to include thirdparty headers conditionally.
885+
886+
Then for export-using style:
887+
888+
.. code-block:: c++
889+
890+
module;
891+
import modules_from_third_party;
892+
#define IN_MODULE_INTERFACE
893+
#include "header_1.h"
894+
#include "header_2.h"
895+
...
896+
#include "header_n.h"
897+
export module your_library;
898+
export namespace your_namespace {
899+
using decl_1;
900+
using decl_2;
901+
...
902+
using decl_n;
903+
}
904+
905+
For export extern-C++ style:
906+
907+
.. code-block:: c++
908+
909+
export module your_library;
910+
import modules_from_third_party;
911+
#define IN_MODULE_INTERFACE
912+
extern "C++" {
913+
#include "header_1.h"
914+
#include "header_2.h"
915+
...
916+
#include "header_n.h"
917+
}
918+
919+
For ABI breaking style,
920+
921+
.. code-block:: c++
922+
923+
export module your_library;
924+
import modules_from_third_party;
925+
#define IN_MODULE_INTERFACE
926+
#include "header_1.h"
927+
#include "header_2.h"
928+
...
929+
#include "header_n.h"
930+
931+
#if the number of .cpp files in your project are small
932+
module :private;
933+
#include "source_1.cpp"
934+
#include "source_2.cpp"
935+
...
936+
#include "source_n.cpp"
937+
#endif
938+
939+
We don't need the non-exported using declarations if we're using implementation module
940+
units now. We can import thirdparty modules directly in the implementation module
941+
units.
942+
943+
Partial dependent libraries providing modules
944+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
945+
946+
In this case, we have to mix the use of ``include`` and ``import`` in the module of our
947+
library. The key point here is still to remove duplicated declarations in translation
948+
units as much as possible. If the imported modules provide headers to skip parsing their
949+
headers, we should include that after the including. If the imported modules don't provide
950+
the headers, we can make it ourselves if we still want to optimize it.
951+
613952
Known Problems
614953
--------------
615954

0 commit comments

Comments
 (0)