Here we return to our user
trees in
doc/example/general/user
. In our user
branch, we have modified the project-lib
library to make use of private interfaces. If you look at the
Abuild.conf
in the src
directory, you will see that it lists
private in its
supported-flags key:
general/user/project/lib/src/Abuild.conf
name: project-lib.src platform-types: native deps: common-lib1 supported-flags: private
In its Abuild.interface
file, it adds
../private-include
to
INCLUDES
only when the
private flag is set:
general/user/project/lib/src/Abuild.interface
INCLUDES = ../include flag private INCLUDES = ../private-include LIBDIRS = $(ABUILD_OUTPUT_DIR) LIBS = project-lib
This makes the headers in the
private-include
directory visible to it and
any build item that depends on it with
-flag=private
.
[47]
Note that the
project-lib.src
build item didn't have to
do anything special to see its own private interfaces. This is
because a build item automatically operates with all of its own
interface flags set for itself. Another thing we've done in this
build item is to put the new source file
ProjectLib_private.cpp
in a
private
subdirectory:
general/user/project/lib/src/Abuild.mk
TARGETS_lib := project-lib SRCS_lib_project-lib := \ ProjectLib.cpp \ private/ProjectLib_private.cpp RULES := ccxx
The only reason we did this was to demonstrate that abuild
allows multi-element paths (i.e., paths
with subdirectories in them) in your source variables. Just
avoid putting “..
” anywhere in
the path.
[48]
If you study ProjectLib.cpp
in
user/project/lib/src
, you will notice that
we have included the file
ProjectLib_private.hpp
, which is located in
the private-include
directory, and that we
have called a function that is declared in that file to get the
value with which we initialize cl1
:
general/user/project/lib/private-include/ProjectLib_private.hpp
#ifndef __PROJECTLIB_PRIVATE_HPP__ #define __PROJECTLIB_PRIVATE_HPP__ extern int ProjectLib_private_get_value(); extern void ProjectLib_private_set_value(int); #endif // __PROJECTLIB_PRIVATE_HPP__
general/user/project/lib/src/ProjectLib.cpp
#include "ProjectLib.hpp" #include "ProjectLib_private.hpp" #include <iostream> ProjectLib::ProjectLib() : cl1(ProjectLib_private_get_value()) { } void ProjectLib::hello() { this->cl1.countBackwards(); }
Private interfaces can be particularly useful in any
implementation that hides implementation details from outside
users because it can prevent accidentally accessing restricted
header files. This type of construct is most useful in straight
C code rather than C++ code since C doesn't provide any
encapsulation capability other than use of opaque types defined
in private header files. This is somewhat akin to using the
friend
keyword in C++, except that access to
private interfaces is requested by the accessor rather than the
accessee.
[49]
The test code in main.cpp
in
user/project/lib/test
also calls a function
defined in the private header file. Note that the
Abuild.conf
file in the
test
directory mentions
project-lib.src
explicitly in its
dependency list, and that it is followed by
-flag=private
:
general/user/project/lib/test/Abuild.conf
name: project-lib.test platform-types: native deps: project-lib project-lib.src -flag=private traits: interesting tester -item=project-lib.src
This means that when project-lib.test
reads project-lib.src
's
Abuild.interface
file, any assignments that
are flagged with the private flag will be
processed.
The alert reader may notice that we have also assigned the trait interesting to this build item. Although the build item is somewhat interesting, the primary purpose of doing this is to illustrate the use of a trait without a referent build item and to show how a trait can be added in a specific tree to supplement traits that are available because of our tree dependencies.
[47]
Note that when the private flag is set,
both assignments to
INCLUDES
take effect. To understand why
this is, please see Section 33.7, “Implementation of the Abuild Interface System”.
[48]
Such constructs, if permitted, would potentially cause abuild
to write files outside the output directory. For example, if
you had ../A.cc
as a source file, abuild
would construct
abuild-
as the object file name. Fortunately, abuild actually
detects this case and reports an error.
platform
/../A.o
[49] Since the build item that supports the private flag is also protected by the scope of its name, this gives us an added layer of protection.