Commit 8861bcb7 authored by Chris Cantwell's avatar Chris Cantwell
Browse files

Merge branch 'master' into fix/pkg-fieldutils

parents 64f9742a 84940736
......@@ -3,3 +3,8 @@
path = docs/tutorial
url = git@gitlab.nektar.info:nektar/tutorial
ignore = all
[submodule "docs/developer-guide"]
branch = master
path = docs/developer-guide
url = git@gitlab.nektar.info:nektar/developer-guide
ignore = all
Changelog
=========
v4.5.0
------
**NekMesh**:
- Add periodic boundary condition meshing in 2D (!733)
- Adjust boundary layer thickness in corners in 2D (!739)
- Add non-O BL meshing in 2D (!757)
**Library**
- Added in sum factorisation version for pyramid expansions and orthogonal
expansion in pyramids (!750)
**FieldConvert**:
- Add input module for Semtex field files (!777)
**Documentation**:
- Added the developer-guide repository as a submodule (!751)
v4.4.1
------
**Library**
- Remove m_offset_elmt_id and GetOffsetElmtId which fixed problems in 2D when
quad elements are listed before tri elements (!758)
- Remove the duplicate output of errorutil (!756)
- Fix BLAS CMake dependencies (!763)
- Fix interpolation issue with Lagrange basis functions (!768)
- Fix issue with average fields not working with different polynomial order
fields (!776)
- Fix rounding of integer parameters (!774)
- Fix Hdf5 output in FilterFieldConvert (!781)
- Fixed extreme memory consumption of Interpolator when interpolating from pts
to fld or between different meshes (!783)
- Fix deadlock with HDF5 input (!786)
- Fix missing entriess in LibUtilities::kPointsTypeStr (!792)
**FieldConvert:**
- Fix issue with field ordering in the interppointdatatofld module (!754)
- Fix issue with FieldConvert when range flag used (!761)
- Fix issue when using output-points combined with noequispaced (!775)
- Fix equispacedoutput for 3DH1D with triangles (!787)
**NekMesh**:
- Fix memory consumption issue with Gmsh output (!747, !762)
- Rework meshing control so that if possible viewable meshes will be dumped
when some part of the system fails (!756)
- Add manifold meshing option (!756)
- Fix issue with older rea input files (!765)
- Fix memory leak in variational optimiser, add small optimisations (!785)
- Check the dimensionality of the CAD system before running the 2D generator (!780)
**IncNavierStokesSolver**
- Fix an initialisation issue when using an additional advective field (!779)
v4.4.0
------
**Library**:
......@@ -42,6 +94,7 @@ v4.4.0
- Fix bug in FieldUtils when using half mode expansions (!734)
- Do not read the same fld/pts files again for every variable (!670)
- Fix bug in CMake PETSc detection for Ubuntu 16.04/Debian 9 (!735)
- Fix warnings with Intel compiler (!742)
**ADRSolver:**
- Add a projection equation system for C^0 projections (!675)
......@@ -100,9 +153,9 @@ v4.4.0
- Change variable names in mcf file to make more sense (!736)
- Fix issues in varopti module so that in can be compiled without meshgen on
(!736)
- Replace LAPACK Eigenvalue calculation with handwritten function in
- Replace LAPACK Eigenvalue calculation with handwritten function in
varopti (!738)
- Improved node-colouring algorithm for better load-balancing
- Improved node-colouring algorithm for better load-balancing
in varopti (!738)
- Simplified calculation of the energy functional in varopti for improved
performance (!738)
......
......@@ -44,7 +44,9 @@ project. It's a pretty simple process:
diff and are not quite ready to merge, use the `[WIP]` tag in the title to
prevent your code from being accidentally merged.
5. Put a comment in the MR saying that it's ready to be merged.
6. Respond to any comments in the code review.
6. If your branch is a minor fix that could appear in the next patch release,
then add the `Proposed patch` label to the merge request.
7. Respond to any comments in the code review.
## Submission checklist
- Did you add regression tests (for fixes) or unit tests and/or normal tests for
......@@ -155,6 +157,78 @@ stick to the following process:
- Once feedback received from the branch author (if necessary) and reviewers are
happy, the branch will be merged.
## Release branches
Nektar++ releases are versioned in the standard form `x.y.z` where `x` is a
major release, `y` a minor release and `z` a patch release:
- major releases are extremely infrequent (on the order of every 2-3 years) and
denote major changes in functionality and the API;
- minor releases occur around twice per year and contain new features with minor
API changes;
- patch releases are targeted on roughly a monthly basis and are intended to
fix minor issues in the code.
The repository contains a number of _release branches_ named `release/x.y` for
each minor release, which are intended to contain **fixes and very minor
changes** from `master` and which form the next patch release. This allows us to
use `master` for the next minor release, whilst still having key fixes in patch
releases.
### Cherry-picking process
Any branches that are marked with the `Proposed patch` label should follow the
following additional steps to cherry pick commits into the `release/x.y` branch.
1. If the branch is on a remote other than `nektar/nektar`, make sure that's
added to your local repository.
2. On a local terminal, run `git fetch --all` to pull the latest changes. It's
important for the commands below that you do this _before_ you merge the
branch into `master`.
3. Merge the branch into master as usual using GitLab.
4. Switch to the appropriate branch with `git checkout release/x.y` and update
with `git pull`.
5. Now check the list of commits to cherry-pick.
```bash
git log --oneline --no-merges --reverse origin/master..REMOTE/fix/BRANCHNAME
```
where `REMOTE` is the remote on which the branch lives and `BRANCHNAME` is
the fix branch. If the list is empty, you probably did a `git fetch` after
you merged the branch into `master`; in this case use `origin/master^`.
6. If you're happy with the list (compare to the MR list on the GitLab MR if
necessary), cherry-pick the commits with the command:
```bash
git cherry-pick -x $(git rev-list --no-merges --reverse origin/master..REMOTE/fix/BRANCHNAME)
```
7. It's likely you'll encounter some conflicts, particularly with the
`CHANGELOG`. To fix these:
- `git status` to see what's broken
- Fix appropriately
- `git commit -a` to commit your fix
- `git cherry-pick --continue`
8. If everything becomes horribly broken, `git cherry-pick --abort`.
9. Once you're happy, `git push` to send your changes back to GitLab.
Steps 5 and 6 can be simplified by creating a script
```bash
#!/bin/bash
src=$1
logopts="--oneline --no-merges --reverse"
commits=`git log $logopts master..$1 | cut -f 1 -d " " | xargs`
echo "Will cherry-pick the following commits: $commits"
echo "Press ENTER to continue..."
read
cherryopts="-x --allow-empty --allow-empty-message"
git cherry-pick $cherryopts $commits
```
which accepts the name of the source branch as the sole argument.
## Formatting guidelines
Nektar++ uses C++, a language notorious for being easy to make obtuse and
difficult to follow code. To hopefully alleviate this problem, there are a
......
......@@ -10,13 +10,14 @@ OPTION(NEKTAR_USE_FFTW
"Use FFTW routines for performing the Fast Fourier Transform." OFF)
IF (NEKTAR_USE_FFTW)
# Set some common FFTW search paths.
# Set some common FFTW search paths for the library.
SET(FFTW_SEARCH_PATHS $ENV{LD_LIBRARY_PATH} $ENV{FFTW_HOME}/lib)
FIND_LIBRARY(FFTW_LIBRARY NAMES fftw3 fftw3f PATHS ${FFTW_SEARCH_PATHS})
IF (FFTW_LIBRARY)
GET_FILENAME_COMPONENT(FFTW_PATH ${FFTW_LIBRARY} PATH)
SET(FFTW_INCLUDE_DIR ${FFTW_PATH}/../include CACHE FILEPATH "FFTW include directory.")
FIND_PATH(FFTW_INCLUDE_DIR NAMES fftw3.h CACHE FILEPATH
"FFTW include directory.")
IF (FFTW_LIBRARY AND FFTW_INCLUDE_DIR)
SET(BUILD_FFTW OFF)
ELSE()
SET(BUILD_FFTW ON)
......@@ -55,9 +56,18 @@ IF (NEKTAR_USE_FFTW)
MESSAGE(STATUS "Found FFTW: ${FFTW_LIBRARY}")
SET(FFTW_CONFIG_INCLUDE_DIR ${FFTW_INCLUDE_DIR})
ENDIF()
# Test if FFTW path is a system path. Only add to include path if not an
# implicitly defined CXX include path (due to GCC 6.x now providing its own
# version of some C header files and -isystem reorders include paths).
GET_FILENAME_COMPONENT(X ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES} ABSOLUTE)
GET_FILENAME_COMPONENT(Y ${FFTW_INCLUDE_DIR} ABSOLUTE)
IF (NOT Y MATCHES ".*${X}.*")
INCLUDE_DIRECTORIES(SYSTEM ${FFTW_INCLUDE_DIR})
ENDIF()
MARK_AS_ADVANCED(FFTW_LIBRARY)
MARK_AS_ADVANCED(FFTW_INCLUDE_DIR)
ENDIF( NEKTAR_USE_FFTW )
INCLUDE_DIRECTORIES(SYSTEM ${FFTW_INCLUDE_DIR})
MARK_AS_ADVANCED(FFTW_LIBRARY)
MARK_AS_ADVANCED(FFTW_INCLUDE_DIR)
ADD_SUBDIRECTORY(user-guide)
ADD_SUBDIRECTORY(developer-guide)
IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/developer-guide/CMakeLists.txt)
ADD_SUBDIRECTORY(developer-guide)
ENDIF ()
IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tutorial/CMakeLists.txt)
ADD_SUBDIRECTORY(tutorial)
......
Subproject commit e128cfaffbbd37c734a667cdc2a07b6f06291615
SET(DEVGUIDESRC ${CMAKE_CURRENT_SOURCE_DIR})
SET(DEVGUIDE ${CMAKE_BINARY_DIR}/docs/developer-guide)
FILE(MAKE_DIRECTORY ${DEVGUIDE}/html)
FIND_PROGRAM(HTLATEX htlatex)
ADD_CUSTOM_TARGET(developer-guide-html
export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${DEVGUIDESRC}//: &&
${HTLATEX} ${DEVGUIDESRC}/developer-guide.tex
"${DEVGUIDESRC}/styling.cfg,html,3,next,NoFonts"
WORKING_DIRECTORY ${DEVGUIDE}/html
)
# If tex4ht successful, create img dir and copy images across
FILE(GLOB_RECURSE imgfiles "img/*.png" "img/*.jpg" "*/img/*.png" "*/img/*.jpg")
ADD_CUSTOM_COMMAND(TARGET developer-guide-html
POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${DEVGUIDE}/html/img)
FOREACH(img ${imgfiles})
ADD_CUSTOM_COMMAND(TARGET developer-guide-html
POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${img} ${DEVGUIDE}/html/img)
ENDFOREACH()
FILE(GLOB_RECURSE pdffiles "*/img/*.pdf")
FIND_PROGRAM(CONVERT convert)
FOREACH(pdf ${pdffiles})
GET_FILENAME_COMPONENT(BASENAME ${pdf} NAME_WE)
ADD_CUSTOM_COMMAND(TARGET developer-guide-html
POST_BUILD COMMAND
${CONVERT} ${pdf} ${DEVGUIDE}/html/img/${BASENAME}.png)
ENDFOREACH()
FIND_PROGRAM(PDFLATEX pdflatex)
FIND_PROGRAM(BIBTEX bibtex)
FIND_PROGRAM(MAKEINDEX makeindex)
ADD_CUSTOM_TARGET(developer-guide-pdf
export TEXINPUTS=${CMAKE_SOURCE_DIR}//: &&
${PDFLATEX} --output-directory ${DEVGUIDE} ${DEVGUIDESRC}/developer-guide.tex
COMMAND TEXMFOUTPUT=${DEVGUIDE} ${BIBTEX} ${DEVGUIDE}/developer-guide.aux
COMMAND TEXMFOUTPUT=${DEVGUIDE} ${MAKEINDEX} ${DEVGUIDE}/developer-guide.idx
COMMAND TEXINPUTS=${CMAKE_SOURCE_DIR}//:
${PDFLATEX} --output-directory ${DEVGUIDE} ${DEVGUIDESRC}/developer-guide.tex
COMMAND TEXINPUTS=${CMAKE_SOURCE_DIR}//:
${PDFLATEX} --output-directory ${DEVGUIDE} ${DEVGUIDESRC}/developer-guide.tex
WORKING_DIRECTORY ${DEVGUIDESRC}
)
\chapter{Coding Standard}
The purpose of this page is to detail the coding standards of the project which
all contributers are requested to follow.
This page describes the coding style standard for C++. A coding style standard
defines the visual layout of source code. Presenting source code in a uniform
fashion facilitates the use of code by different developers. In addition,
following a standard prevents certain types of coding errors.
All of the items below, unless otherwise noted, are guidelines. They are
recommendations about how to lay out a given block of code. Use common sense and
provide comments to describe any deviation from the standard. Sometimes,
violating a guideline may actually improve readability.
If you are working with code that does not follow the standard, bring the code
up-to-date or follow the existing style. Don’t mix styles.
\section{Code Layout}
The aim here is to maximise readability on all platforms and editors.
\begin{itemize}
\item Code width of 80 characters maximum - hard-wrap longer lines.
\item Use sensible wrapping for long statements in a way which maximises
readability.
\item Do not put multiple statements on the same line.
\item Do not declare multiple variables on the same line.
\item Provide a default value on all variable declarations.
\item Enclose every program block (if, else, for, while, etc) in braces, even if
empty or just a single line.
\item Opening braces (\{) should be on their own line.
\item Braces at same indentation as preceeding statement.
\item One class per .cpp and .h file only, unless nested.
\item Define member functions in the .cpp file in the same order as defined in
the .h file.
\item Templated classes defined and implemented in a single .hpp file.
\item Do not put inline functions in the header file unless the function is
trivial (e.g. accessor, empty destructor), or profiling explicitly suggests to.
\item Inline functions should be declared within the class declaration but
defined outside the class declaration at the bottom of the header file.
\begin{notebox}
Virtual and inline are mutually exclusive. Virtual functions should therefore be
implemented in the .cpp file.
\end{notebox}
\end{itemize}
\section{Space}
Adding an appropriate amount of white space enhances readability. Too much white
space, on the other hand, detracts from that readability.
\begin{itemize}
\item Indent using a four-space tab. Consistent tab spacing is necessary to
maintain formatting. Note that this means when a tab is pressed, four physical spaces are
inserted into the source instead.
\item Put a blank line at the end of a public/protected/private block.
\item Put a blank line at the end of every file.
\item Put a space after every keyword (if, while, for, etc.).
\item Put a space after every comma, unless the comma is at the end of the line.
\item Do not put a space before the opening parenthesis of an argument list to a
function.
\item Declare pointers and references with the * or \& symbol next to the
declarator, not the type; e.g., Object *object. Do not put multiple variables in the same
declaration.
\item Place a space on both sides of a binary operator.
\item Do not use a space to separate a unary operator from its operand.
\item Place open and close braces on their own line. No executable statements
should appear on the line with the brace, but comments are allowed. Indent opening
braces at the same level as the statement above and indent the closing brace at
the same level as the corresponding opening brace.
\item Indent all statements following an open brace by one tab. Developer Studio
puts any specifier terminated with a colon at the same indentation level as the
enclosing brace. Examples of such specifiers include case statements, access
specifiers (public, private, protected), and goto labels. This is not acceptable
and should be manually corrected so that all statements appearing within a block
and delineated by braces are indented.
\item Break a line into multiple lines when it becomes too long to read. Use at
least two tabs to start the new line, so it does not look like the start of a
block.
\item Follow C++ style comments with one space. It is also preferable to
consider any text that follows C++ style comments as a sentence and to begin this text
with a capital letter. This helps to distinguish the line from a continuation of
a previous line; i.e., \inlsh{// This is my comment.}
\item As a general rule, don’t keep commented out source code in the final
baselined product. Such code leads the reader to believe there was uncertainty
in the code as it currently exists.
\item Place the \# of a preprocessor directive at column one. An exception is
the use of nested ifdefs where the bodies only contain other preprocessor directives.
Add tabs to enhance readability:
\begin{lstlisting}[style=C++Style]
void foo() {
for(int i = 0; i < 10; ++i)
{
#ifdef BAR
do_something();
#endif
for_loop_code();
}
}
\end{lstlisting}
\item Use tabular white space if it enhances
readability.
\item Use only one return statement. Structure the code so that only one return
statement is necessary.
\end{itemize}
\section{Naming Conventions}
Keep variable and function names meaningful but concise.
\begin{itemize}
\item Begin variable names with lower-case letter.
\item Begin function names and class names with upper-case letter.
\item All function, class and variable names should be written in CamelCase,
e.g. \inlsh{MyClass, DoFunction() or myVariableName}.
\item All preprocessor definitions written in UPPER\_CASE with words separated
by underscores, e.g. USE\_SPECIFIC\_FEATURE.
\item All member variables prefixed with m\_.
\item All constants prefixed with a k.
\item All function parameters prefixed with a p.
\item All enumerations prefixed with an e.
\item Do not use leading underscores.
\end{itemize}
\section{Namespaces}
The top-level namespace is "Nektar". All code should reside in this namespace or
a sub-space of this.
\begin{itemize}
\item Namespaces correspond to code structure.
\item Namespaces should be kept to a minimum to simplify the interface to their
contents.
\end{itemize}
\section{Documentation}
\begin{itemize}
\item Briefs for classes, functions and types in header files using
\inlsh{///} notation.
\item Full documentation with implementation using \inlsh{/** ... *\/}
notation.
\item Use @ symbol for @class, @param, @returns, etc for ease of identification.
\item Any separate documentation pages not directly associated with a portion of
the code should be in a separate file in /docs/html/doxygen.
\end{itemize}
\chapter{Core Concepts}
This section describes some of the key concepts which are useful when developing
code within the Nektar++ framework.
\section{Factory method pattern}
The factory method pattern is used extensively throughout Nektar++ as a
mechanism to instantiate objects. It provides the following benefits:
\begin{itemize}
\item Encourages modularisation of code such that conceptually related
algorithms are grouped together
\item Structuring of code such that different implementations of the same
concept are encapsulated and share a common interface
\item Users of a factory-instantiated modules need only be concerned with the
interface and not the details of underlying implementations
\item Simplifies debugging since code relating to a specific implementation
resides in a single class
\item The code is naturally decoupled to reduce header-file dependencies and
improves compile times
\item Enables implementations (e.g. relating to third-party libraries) to be
disabled through the build process (CMake) by not compiling a specific
implementation, rather than scattering preprocessing statements throughout the
code
\end{itemize}
For conceptual details see the Wikipedia page.
%\url{http://en.wikipedia.org/wiki/Factory_pattern}.
\subsection{Using NekFactory}
The templated NekFactory class implements the factory pattern in Nektar++.
There are two distinct aspects to creating a factory-instantiated collection of
classes: defining the public interface, and registering specific
implementations. Both of these involve adding standard boilerplate code. It is
assumed that we are writing a code which implements a particular concept or
functionality within the code, for which there are multiple implementations. The
reasons for multiple implementations may be very low level such as alternative
algorithms for solving a linear system, or high level, such as selecting from a
range of PDEs to solve.
\subsubsection{Creating an interface (base class)}
A base class must be defined which prescribes an implementation-independent
interface. In Nektar++, the template method pattern is used, requiring public
interface functions to be defined which call private virtual implementation
methods. The latter will be overridden in the specific implementation classes.
In the base class these virtual methods should be defined as pure virtual, since
there is no implementation and we will not be instantiating this base class
explicitly.
As an example we will create a factory for instantiating different
implementations of some concept \inlsh{MyConcept}, defined in
\inlsh{MyConcept.h} and \inlsh{MyConcept.cpp}. First in \inlsh{MyConcept.h},
we need to include the NekFactory header
\begin{lstlisting}[style=C++Style]
#include <LibUtilities/BasicUtils/NekFactory.hpp>
\end{lstlisting}
The following code should then be included just before the base class
declaration (in the same namespace as the class):
\begin{lstlisting}[style=C++Style]
class MyConcept
// Datatype for the MyConcept factory
typedef LibUtilities::NekFactory< std::string, MyConcept,
ParamType1,
ParamType2 > MyConceptFactory;
MyConceptFactory& GetMyConceptFactory();
\end{lstlisting}
The template parameters define the datatype of the key used to retrieve a
particular implementation (usually a string, enum or custom class such as
\inlsh{MyConceptKey}, the base class (in our case \inlsh{MyConcept} and a list
of zero or more parameters which are taken by the constructors of all
implementations of the type \inlsh{MyConcept} (in our case we have two). Note
that all implementations must take the same parameter list in their constructors.
The normal definition of our base class then follows:
\begin{lstlisting}[style=C++Style]
class MyConcept
{
public:
MyConcept(ParamType1 p1, ParamType2 p2);
...
};
\end{lstlisting}
We must also define a shared pointer for our base class for use later
\begin{lstlisting}[style=C++Style]
typedef boost::shared_ptr<MyConcept> MyConceptShPtr;
\end{lstlisting}
\subsubsection{Creating a specific implementation (derived class)}
A class is defined for each specific implementation of a concept. It is these
specific implementations which are instantiated by the factory.
In our example we will have an implementations called \inlsh{MyConceptImpl1}
defined in \inlsh{MyConceptImpl1.h} and \inlsh{MyConceptImpl1.cpp}. In the
header file we include the base class header file
\begin{lstlisting}[style=C++Style]
#include <Subdir/MyConcept.h>
\end{lstlisting}
We then define the derived class as normal:
\begin{lstlisting}[style=C++Style]
class MyConceptImpl1 : public MyConcept
{
...
};
\end{lstlisting}
In order for the factory to work, it must know
\begin{itemize}
\item that {{{MyConceptImpl1}}} exists, and
\item how to create it.
\end{itemize}
To allow the factory to create instances of our class we define a function in
our class:
\begin{lstlisting}[style=C++Style]
/// Creates an instance of this class
static MyConceptSharedPtr create(
ParamType1 p1,
ParamType2 p2)
{
return MemoryManager<MyConceptImpl1>::AllocateSharedPtr(p1, p2);
}
\end{lstlisting}
This function simply creates an instance of \inlsh{MyConceptImpl1} using the
supplied parameters. It must be \inlsh{static} because we are not operating on
an existing instance and it should return a base class shared pointer (rather
than a \inlsh{MyConceptImpl1} shared pointer), since the point of the factory
is that the calling code does not know about specific implementations.
The last task is to register our implementation with the factory. This is done
using the \inlsh{RegisterCreatorFunction} member function of the factory.
However, we wish this to happen as early on as possible (so we can use the
factory straight away) and without needing to explicitly call the function for
every implementation at the beginning of our program (since this would again
defeat the point of a factory)! The solution is to use the function to
initialise a static variable: it will be executed prior to the start of the
\inlsh{main()} routine, and can be located within the very class it is
registering, satisfying our code decoupling requirements.
In \inlsh{MyConceptImpl1.h} we define a static variable with the same datatype
as the key used in our factory (in our case \inlsh{std::string})
\begin{lstlisting}[style=C++Style]
static std::string className;
\end{lstlisting}
The above variable can be \inlsh{private} since it is typically never actually
used within the code. We then initialise it in \inlsh{MyConceptImpl1.cpp}
\begin{lstlisting}[style=C++Style]
string MyConceptImpl1::className
= GetMyConceptFactory().RegisterCreatorFunction(
"Impl1",
MyConceptImpl1::create,
"First implementation of my concept.");
\end{lstlisting}
The first parameter specifies the value of the key which should be used to
select this implementation. The second parameter is a function pointer to our
static function used to instantiate our class. The third parameter provides a
description which can be printed when listing the available MyConcept
implementations.
\subsection{Instantiating classes}
To create instances of MyConcept implementations elsewhere in the code, we must
first include the ''base class'' header file
\begin{lstlisting}[style=C++Style]
#include <Subdir/MyConcept.h>
\end{lstlisting}
Note we do not include the header files for the specific MyConcept
implementations anywhere in the code (apart from \inlsh{MyConceptImpl1.cpp}).
If we modify the implementation, only the implementation itself requires
recompiling and the executable relinking.
We create an instance by retrieving the \inlsh{MyConceptFactory} and call the
\inlsh{CreateInstance} member function of the factory:
\begin{lstlisting}[style=C++Style]
ParamType p1 = ...;
ParamType p2 = ...;
MyConceptShPtr p = GetMyConceptFactory().CreateInstance( "Impl1", p1, p2 );
\end{lstlisting}
Note that the class is used through the pointer \inlsh{p}, which is of type
\inlsh{MyConceptShPtr}, allowing the use of any of the public interface
functions in the base class (and therefore the specific implementations behind them) to be
called, but not directly any functions declared solely in a specific
implementation.