We observe that
- The software is designed to solve a family of problems.
- The problems solved involve several topics.
- The same algorithm can be used in several places.
- Some algorithms or implementations can be used in other software.
We observe that
std::vector<double> x(101), y(201);
double a = 0, b = 1;
double c = 0, d = 1;
for(std::size_t i = 0; i<101; ++i)
{
double dx = (b - a)/100;
x[i] = i*dx + a;
}
for(std::size_t i = 0; i<201; ++i)
{
double dx = (d - c)/200;
y[i] = i*dx + c;
}
std::vector<std::vector<double>> v(101, std::vector<double>(201));
for(std::size_t i=0; i<101; i++)
{
for(std::size_t j=0; j<201; j++)
{
v[i][j] = std::exp(-100*(x[i]*x[i] + y[j]*y[j]));
}
}
auto x = linspace(0, 1, 101);
auto y = linspace(0, 1, 201);
auto v = init_on_meshgrid(x, y, [](auto x, auto y){return std::exp(-100*(x*x + y*y));});
or
auto v = init_on_meshgrid(linspace(0, 1, 101), linspace(0, 1, 201),
[](auto x, auto y){return std::exp(-100*(x*x + y*y));});
auto linspace(double start, double stop, std::size_t num)
{
std::vector<double> x(num);
double step = (stop - start)/(num-1);
for(std::size_t i=0; i<num; ++i)
{
x[i] = start + step*i;
}
return x;
}
#include <algorithm>
auto linspace(double start, double stop, std::size_t num)
{
std::vector<double> x(num);
double step = (stop - start)/(num-1);
std::generate(x.begin(), x.end(), [step, i = 0]() mutable {return start + step*(i++);});
return x;
}
template<class Func>
auto init_on_meshgrid(const std::vector<double>& x, const std::vector<double>& y, Func&& f)
{
std::vector<std::vector<double>> output(x.size(), std::vector<double>(y.size()));
for(std::size_t i = 0; i < x.size(); ++i)
{
for(std::size_t j = 0; j < y.size(); ++j)
{
output[i][j] = f(x[i], y[j]);
}
}
return output;
}
These two functions should be in separate files.
A declaration introduces a name which denotes an entity.
An entity can be
All definitions are declarations.
Not all declarations are definitions.
int f();
using array_t = std::vector<double>;
typedef integer int;
extern int i;
struct poly
{
double apply(double x);
}
int f()
{
return 42;
}
int i;
extern int i = 42;
double poly::apply(double x)
{
return x*x + 2*x + 1;
}
Other possibilities will be seen throughout this course.
text files with the extensions .cpp, .cxx, .C
text files with the extensions .hpp, .h
By convention, source files contain the definitions and header files contain the declarations.
gcc
clang
Intel compiler
gcc
clang
Xcode
Intel compiler
Visual Studio
MinGW
Intel compiler
Compile your first program using gcc
following the given instructions in the notebook
g++ -c source_1.cpp source_2.cpp
g++ -c source_1.cpp source_2.cpp -I/PATH/TO/INCLUDE/FILES
g++ source_1.o source_2.o -o my_app.exe
g++ source_1.o source_2.o -o my_app.exe -L/PATH/TO/LIBRARY/FILES -lmylib.a
A translation unit can contain only one definition of certain entities (variable, function, class, ...).
But multiple declarations are allowed.
A given program must contain exactly one definition for every non-inline variable or function that is used in the program.
For an inline variable or an inline function, a definition is required in every translation unit that uses it.
Exactly one definition of a class must appear in any translation unit that uses it in such a way that the class must be complete.
int i;
int i;
extern int i;
int i;
int func(int i);
int func(int i)
{
return i*i;
}
int func(int i)
{
return i*i;
}
int func(int i)
{
return i*i;
}
double square(double x)
{
return x*x;
}
#include <cmath>
#include "square.hpp"
double gaussian(double x, double y)
{
return std::exp(-100*(square(x) + square(y)));
}
#include "square.hpp"
#include "gaussian.hpp"
int main()
{
double x = gaussian(1, 2);
}
Multiple definition of square !!!
#ifndef SQUARE_HPP
#define SQUARE_HPP
double square(double x)
{
return x*x;
}
#endif
#ifndef GAUSSIAN_HPP
#define GAUSSIAN_HPP
#include <cmath>
#include "square.hpp"
double gaussian(double x, double y)
{
return std::exp(-100*(square(x) + square(y)));
}
#endif
namespace provide a method for preventing name conflicts in large project.
namespace algorithm
{
namespace insertion
{
void sort(std::vector<double>& x)
{
...
}
}
namespace merge
{
void sort(std::vector<double>& x)
{
...
}
}
}
algorithm::merge::sort(x);
namespace mg = algorithm::merge; // namespace alias
mg.sort(x);
using algorithm::merge::sort; // using directive
sort(x);
using namespace algorithm::merge; // evil !
sort(x);
using namespace std; // ultimate evil !!
int my_var = 6;
int my_var = 8;
Link error: multiple definitions of my_var
static int my_var = 6;
static int my_var = 8;
OK: A static variable is local to a translation unit
static int my_var = 6;
static int my_var = 8;
Equivalent to
namespace
{
int my_var = 6;
}
namespace
{
int my_var = 8;
}
Anonymous namespace encapsulates definitions
int func()
{
int res = 0;
return ++res;
}
int func()
{
static int res = 0;
return ++res;
}
Go back to the Jupyter notebook and follow the instructions
project(myapp)
cmake_minimum_required(VERSION 3.15)
# create an executable
add_executable(my_app src/source.cpp include/header.hpp)
CMakeLists.txt
cmake -B build . -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/my_app
CMAKE_BUILD_TYPE flag can be Debug, Release or RelWithDebInfo
project(myapp)
cmake_minimum_required(VERSION 3.15)
find_package(other_lib REQUIRED)
add_subdirectory(src)
# create an executable
add_executable(my_app src/my_app.cpp)
target_link_library(my_app PRIVATE my_lib other_lib)
CMakeLists.txt
# create a library
add_library(my_lib STATIC src/source_1.cpp)
target_include_directory(my_lib PUBLIC include)
target_compile_features(my_lib PUBLIC cxx_std_17)
src/CMakeLists.txt