C/C++

How to iterate TopTools_ListOfShape in OpenCASCADE

TopTools_ListOfShape is a NCollection_List<T> and hence supports iteration using the C++11 foreach loop (also called range-based for loop):
TopTools_ListOfShape myShapes = /* ... */;

// Iterate myShapes
for(const TopoDS_Shape& shape : myShapes) {
    /* ... */
}

Remember to compile with at least --std=c++11 or equivalent for your compiler to allow using the range-based for loop.

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate NCollection_List in OpenCASCADE

NCollection_List<T> supports iteration using the C++11 foreach loop (also called range-based for loop):
NCollection_List<T> myList = /* ... */;

// Iterate myList
for(const T& value : myList) {
    /* ... */
}

Remember to compile with at least --std=c++11 or equivalent for your compiler to allow using the range-based for loop.

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate all edges in TopoDS_Face using OpenCASCADE

You can use the OCCUtils library to iterate all TopoDS_Face instances in a TopoDS_Solid

#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto edges = ShapeComponents::AllEdgesWithin(myShape);

// Iterate all solids
for(const TopoDS_Edge& edge : edges) {
    /* ... */
}

Alternatively, you can use this raw OpenCASCADE source code without OCCUtils:

#include <TopTools_IndexedMapOfShape.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp.hxx>

TopoDS_Shape myShape = /* ... */;

TopTools_IndexedMapOfShape faces;
TopExp::MapShapes (myShape, TopAbs_EDGE, faces);

for (int i = 1; i <= faces.Extent (); i++) {
    TopoDS_Edge face = TopoDS::Edge(edges(i));
    /* ... */
}

If you have the choice, I recommend using OCCUtils since it makes your code much more readable than using the raw OpenCASCADE API.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate all faces in TopoDS_Solid using OpenCASCADE

You can use the OCCUtils library to iterate all TopoDS_Face instances in a TopoDS_Solid

#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto faces = ShapeComponents::AllFacesWithin(myShape);

// Iterate all solids
for(const TopoDS_Face& face : faces) {
    /* ... */
}

Alternatively, you can use this source code without OCCUtils:

#include <TopTools_IndexedMapOfShape.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp.hxx>

TopoDS_Shape myShape = /* ... */;

TopTools_IndexedMapOfShape faces;
TopExp::MapShapes (myShape, TopAbs_FACE, faces);

for (int i = 1; i <= faces.Extent (); i++) {
    TopoDS_Face face = TopoDS::Face(faces(i));
    /* ... */
}

If you have the choice, I recommend using OCCUtils since it makes your code much more readable than using the raw OpenCASCADE API.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to get TopoDS_Solid(s) from TopoDS_Shape in OpenCASCADE

There are two important cases if you want to get a TopoDS_Solid object from a TopoDS_Shape:

A: The TopoDS_Shape is a solid

You can check if that is the case using

#include <TopoDS_Shape.hxx>

TopoDS_Shape myShape = /* ... */

bool isSolid = myShape.ShapeType() == TopAbs_SOLID;

Alternatively, you can use the Shape::IsSolid() function from OCCUtils:

#include <occutils/Shape.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

if(Shape::IsSolid(myShape)) {
    /* ... */
}

If it’s a solid, you can convert it to a TopoDS_Solid directly using:

TopoDS::Solid(myShape);

Full example:

#include <TopoDS_Shape.hxx>
#include <TopoDS.hxx>
#include <occutils/Shape.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

if (Shape::IsSolid(myShape)) {
    TopoDS_Solid solid = TopoDS::Solid(myShape);
    /* ... */
}

B: The shape is not itself a TopoDS_Solid but contains one or more TopoDS_Solids

This is often the case for TopoDS_Compounds. Use Shape::IsCompound() from OCCUtils to check if your TopoDS_Shape is actually a TopoDS_Compound.

#include <occutils/Shape.hxx>
using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;
if(Shape::IsCompound(myShape)) { /* ... */ }

Using OCCUtils, you have now several options to get the TopoDS_Solids from the Compound:

If there is a chance that there are zero or more than one TopoDS_Solids in your compound:

Use ShapeComponents::AllSolidsWithin() from OCCUtils to get a std::vector<TopoDS_Solid> of all solids within the TopoDS_Compound:

#include <occutils/Shape.hxx>
#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto solids = ShapeComponents::AllSolidsWithin(myShape);

// Iterate all solids
for(const TopoDS_Solid& solid : solids) {
    /* ... */
}

If you are sure the compound only contains one solid (also works if it’s a solid itself):

Use ShapeComponents::TryGetSingleSolid(shape) which returns a std::optional<TopoDS_Solid> (no value if there is no solid or there are multiple solids in shape) or ShapeComponents::GetSingleSolid(shape) which throws an exception if there is no solid or there are multiple solids in the shape.

Example:

#include <occutils/ShapeComponents.hxx>
#include <iostream> // cerr, endl

using namespace std;
using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto solidOpt = ShapeComponents::TryGetSingleSolid(myShape);

if(solidOpt.has_value()) {
    TopoDS_Solid solid = solidOpt.value();
} else {
    cerr << "No solid or multiple solids in shape!" << endl;
}

If you want to use pure OpenCASCADE without OCCUtils:

Use this snippet to directly iterate the TopoDS_Solid instances in the shape

TopTools_IndexedMapOfShape solidShapes;
TopExp::MapShapes (myShape, TopAbs_SOLID, solidShapes);

for (int i = 1; i <= solidShapes.Extent (); i++) {
    TopoDS_Solid solid = TopoDS::Solid(solidShapes(i));
    /* Do what you need with the solid here ! */
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

Quick tip: How to return std::optional from a function

Here’s a template for returning a std::optional from a function:

#include <optional>
// Remember to compile with --std=c++17 or equivalent

std::optional<double> myOptionalFunction() {
    auto myValue = /* ... */;
    if(myValue.IsNull()) {
        // We don't have a value
        return std::nullopt; // .has_value() => false
    }
    // We have a value, so return the value
    return myValue.AsDouble(); // .has_value() => true
}

std::nullopt is the easy way to return no value as a std::optional. Using this method instead of the default constructor (return std::optional<double>() in this case) you don’t explicitly have to enter the full qualified name including template argument (optional<double> if you use namespace std;). It’s much easier to read and copy&paste this way.

In the last line we can just return a double (myValue.AsDouble() in this example). Why? Because this will implicitly call the implicit constructor of std::optional<double>(const double& value) and therefore convert your double into a std::optional<double>() instance.

Usage example:

#include // std::optional
#include  // std::cerr, std::endl

using namespace std;

std::optional myOptionalFunction() {
    /* Your code goes here ! */
}

int main() {
    auto myOptionalValue = myOptionalFunction;
    if(myOptionalValue.has_value()) {
         // Print error message
         cerr << "The optional has no value!" << endl;
         return;
    }
    // Extract the value
    auto myValue = myOptionalValue.value();
    /* You can do something with myValue here! */
}

Alternatively you can use std::optional<T>::value_or() like this to give either the value or a default value:

#include <optional> // std::optional
#include <iostream> // std::cerr, std::endl

using namespace std;

std::optional<double> myOptionalFunction() {
    /* Your code goes here ! */
}

int main() {
    auto myOptionalValue = myOptionalFunction;
    const double defaultValue = 0.0;
    // Value will be 0.0 if myOptionalFunction() returned <no value>
    auto myValue = myOptionalValue.value_or(defaultValue);
    /* You can do something with myValue here! */
}
Posted by Uli Köhler in C/C++

How to compute surface area of TopoDS_Face in OpenCASCADE

In order to compute the surface area of a TopoDS_Face, in OpenCASCADE, simply use BRepGProp::SurfaceProperties and call .Mass() on the resulting GProp_GProps object:

GProp_GProps gprops;
BRepGProp::SurfaceProperties(face, gprops); // Stores results in gprops
double area = gprops.Mass();

Alternatively, you can use my OCCUtils library which also also providers a lot of other utility functions:

#include <occutils/Surface.hxx>
using namespace OCCUtils;

double Surface::Area(const TopoDS_Shape& face);

Example:

#include <occutils/Surface.hxx>

using namespace OCCUtils;

// ...
double area = Surface::Area(myFace);
// ...

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to fix ‘ft2build.h: No such file or directory’

If your compiler shows you build errors like this:

/usr/include/opencascade/Font_FTLibrary.hxx:23:10: fatal error: ft2build.h: No such file or directory

you are missing the ft2build.h header file from libfreetype.

In order to install it on Ubuntu, use

sudo apt-get install libfreetype6-dev

If the error persists, you might want to add /usr/include/freetype2 to the include path. For GCC/G++, add

-I/usr/include/freetype2

to the command line arguments of the compiler.

If you use CMake, you can use a line like this in your CMakeLists.txt:

include_directories (/usr/include/freetype2)
Posted by Uli Köhler in C/C++

How to change CMake object file suffix from default ‘.o’

In order to configure CMake to use an alternate object file suffix (default: .o on Linux) use these lines in your CMakeLists.txt:

set(CMAKE_C_OUTPUT_EXTENSION ".rel")
set(CMAKE_CXX_OUTPUT_EXTENSION ".rel")

This example changes the output extension from .o to .rel (which is required for the SDCC compiler). Be sure to replace ".rel" by your desired output suffix.

Note that in order for these to take effect, you might need to completely remove CMakeCache.txt, CMakeFiles & cmake_install.cmake:

rm -rf CMakeCache.txt CMakeFiles cmake_install.cmake

 

Posted by Uli Köhler in C/C++, CMake

C++ equivalent of NumPy/PHP rad2deg

In order to convert radians to degrees, PHP provides the rad2deg function whereas Python’s NumPy library provides np.rad2deg.

In C++ there is no standard function to convert radians to degrees.

However, you can use this simple snippet:

#define _USE_MATH_DEFINES
#include <cmath>

/**
 * Convert the angle given in radians to degrees.
 */
template<typename F>
F rad2deg(F angle) {
    return angle * 180.0 / M_PI;
}

This will work for double, float etc. and returns the angle in degrees.

The formula is pretty simple:

\text{Degrees} = \frac{\text{Radians} \cdot 180°}{\pi}
Posted by Uli Köhler in C/C++, Mathematics

How to fix C++ boost/array.hpp:118:61: error: expected primary-expression before ‘,’ token

In a legacy C++ project that is using Boost ProgramOptions. trying to compile it will yield this error message:

In file included from /usr/include/boost/lexical_cast/detail/converter_lexical.hpp:50:0,
                 from /usr/include/boost/lexical_cast/try_lexical_convert.hpp:42,
                 from /usr/include/boost/lexical_cast.hpp:32,
                 from /usr/include/boost/program_options/value_semantic.hpp:14,
                 from /usr/include/boost/program_options/options_description.hpp:13,
                 from /usr/include/boost/program_options.hpp:15,
                 from /home/uli/dev/myproject/datasplit.cpp:15:
/usr/include/boost/array.hpp: In member function ‘T& boost::array<T, N>::operator[](boost::array<T, N>::size_type)’:
/usr/include/boost/array.hpp:118:61: error: expected primary-expression before ‘,’ token
             return BOOST_ASSERT_MSG( i < N, "out of range" ), elems[i];

I didn’t find a satisfying way to fix this issue but it can be worked around fixing the issue in the source file:

First, open /usr/include/boost/array.hpp in your favourite editor as root (sudo!). I use nano.

Then, go to line 118 which reads:

return BOOST_ASSERT_MSG( i < N, "out of range" ), elems[i];

Replace that line by

BOOST_ASSERT_MSG( i < N, "out of range" );
return elems[i];

Also, 4 lines below what we just edited you’ll find another instance of

return BOOST_ASSERT_MSG( i < N, "out of range" ), elems[i];

Also replace that by

BOOST_ASSERT_MSG( i < N, "out of range" );
return elems[i];

Now. save the file and close your editor. Your code should compile now.

Posted by Uli Köhler in C/C++, GCC errors

How to recursively delete directory using C++17 filesystem library

To remove a file or directory (for example my-directory) use remove_all from the C++17 filesystem library:

remove_all("my-directory");

This will remove my-directory and all its sub-directories and files recursively.

Full example:

#include <experimental/filesystem>
using namespace std::experimental::filesystem;

int main() {
    remove_all("my-directory");
}

In case you are using GCC, you need to compile the file like this:

g++ -o delete-cpp17 delete-cpp17.cpp -lstdc++fs

You need to link the stdc++fs library so the functions from the C++17 filesystem library are available to your program.

If you just want to remove a file and don’t want to risk deleting an entire directory tree, use remove instead of remove_all or see our previous post How to delete file using C++17 filesystem library

Posted by Uli Köhler in C/C++

How to delete file using C++17 filesystem library

To remove a file (for example test.txt) use remove from the C++17 filesystem library:

remove("test.txt");

Full example:

#include <experimental/filesystem>
using namespace std::experimental::filesystem;

int main() {
    remove("test.txt");
}

In case you are using GCC, you need to compile the file like this:

g++ -o delete-cpp17 delete-cpp17.cpp -lstdc++fs

You need to link the stdc++fs library so the functions from the C++17 filesystem library are available to your program.

Note that remove does not recursively remove directories! Use remove_all or see How to recursively delete directory using C++17 filesystem library

Posted by Uli Köhler in C/C++

Is it a file or directory? Using C++17 filesystem library

You can use is_regular_file to check any path (either a C++17 path object or just a string). Similarly you can use is_directory to check if the given path belongs to a directory.

Note that these just return false if the file or directory does not exist!

// Check if something is a file
bool isTestTxtAFile = is_regular_file("test.txt"); // true
bool isMyDirectoryAFile = is_regular_file("my-directory"); // false
bool isDoesNotExistAFile = is_regular_file("does-not-exist"); // false
// Check if something is a directory
bool isTestTxtADirectory = is_directory("test.txt"); // false
bool isMyDirectoryADirectory = is_directory("my-directory"); // true
bool isDoesNotExistADirectory = is_directory("does-not-exist"); // false
Posted by Uli Köhler in C/C++

How to recursively list a directory using C++17 filesystem library

Note: To find out how to list a directory non-recursively, just replace recursive_directory_iterator by directory_iterator or see our full post How to list a directory using C++17 filesystem library.

To recursively list a directory using the C++17 filesystem library use this snippet:

#include <experimental/filesystem>

using namespace std::experimental::filesystem;

for(const directory_entry& entry : recursive_directory_iterator("my-directory")) {
    const auto& path = entry.path();
    // ...
}

Full example:

#include <iostream>
#include <string>
#include <experimental/filesystem>

using namespace std;
using namespace std::experimental::filesystem;

int main() {
    for(const directory_entry& entry : recursive_directory_iterator("my-directory")) {
        // Is it a file / directory?
        bool isNormalFile = is_regular_file(entry);
        bool isDirectory = is_directory(entry);

        auto path = entry.path();
        // Path: my-directory/test.txt
        string pathString = path.string();

        // Filename: test.txt
        string filenameString = path.filename().string();

        // Extension: txt
        string extensionString = path.extension().string();

        // NOTE: You can also "cout << path" directly
    }
}

Compile like this:

g++ -o filesystem-example filesystem-example.cpp -lstdc++fs

For this directory:

my-directory
├── subdirectory
│   └── subdir-test.txt
└── test.txt

this will list subdirectory , subdirectory/subdir-test.txt and test.txt.

Posted by Uli Köhler in C/C++

How to list a directory using C++17 filesystem library

To list a directory (non-recursively) using the C++17 filesystem library use this snippet:

#include <experimental/filesystem>

using namespace std::experimental::filesystem;

for(const directory_entry& entry : directory_iterator("my-directory")) {
    const auto& path = entry.path();
    // ...
}

Full example:

#include <iostream>
#include <string>
#include <experimental/filesystem>

using namespace std;
using namespace std::experimental::filesystem;

int main() {
    for(const directory_entry& entry : directory_iterator("my-directory")) {
        // Is it a file / directory?
        bool isNormalFile = is_regular_file(entry);
        bool isDirectory = is_directory(entry);

        auto path = entry.path();
        // Path: my-directory/test.txt
        string pathString = path.string();

        // Filename: test.txt
        string filenameString = path.filename().string();

        // Extension: txt
        string extensionString = path.extension().string();

        // NOTE: You can also "cout << path" directly
    }
}

Compile like this:

g++ -o filesystem-example filesystem-example.cpp -lstdc++fs

For this directory:

my-directory
├── subdirectory
│   └── subdir-test.txt
└── test.txt

this will only list subdirectory and test.txt

Posted by Uli Köhler in C/C++

Are changes made to mmap MAP_PRIVATE visible to the current process?

The mmap mapage tells us that whether changes in memory made to a MAP_PRIVATE-memory-mapped file are visible to the process mapping the file is unspecified (they will not be written to the mapped file nor will they be visible to other processes mapping the same file).

However, we can verify if changes are actually visible on any given system / kernel version using this test program:

#include <string>
#include <iostream> 
#include <fstream> 
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/stat.h>

using namespace std;

int main() {
    string filename = "deleteme.txt";
    // Create file
    ofstream fout(filename.c_str());
    fout << "ABCD";
    fout.close();
    // Create & open underlying file
    int fd = open(filename.c_str(), O_RDONLY);
    assert(fd != -1);
    // mmap with MAP_PRIVATE and allow writes to the mmapped pages
    char* mmappedData = (char*)mmap(NULL, 4, PROT_WRITE, MAP_PRIVATE | MAP_POPULATE, fd, 0);
    assert(mmappedData != MAP_FAILED);
    // Insert data into the MAP_PRIVATE area
    mmappedData[0] = 0;
    mmappedData[1] = 1;
    mmappedData[2] = 2;
    mmappedData[3] = 3;
    // Overwrite once more
    mmappedData[0] = 3;
    mmappedData[1] = 2;
    mmappedData[2] = 1;
    mmappedData[3] = 0;
    // Check data
    if(mmappedData[0] == 3 &&
        mmappedData[1] == 2 &&
        mmappedData[2] == 1 &&
        mmappedData[3] == 0) {
        cout << "Congrats, MAP_PRIVATE changes are reflected in memory!" << endl;
    } else {
        cout << "Nope, MAP_PRIVATE changes are NOT reflected in memory!" << endl;
    }
}

Download, compile and run using:

wget https://techoverflow.net/scripts/mmap-private-check.cpp && g++ -o mmap-private-check mmap-private-check.cpp && ./mmap-private-check

This will either print

Congrats, MAP_PRIVATE changes are reflected in memory!

(I only got this result for every Linux system I tested on, e.g. on Ubuntu 18.04) or

Nope, MAP_PRIVATE changes are NOT reflected in memory!

Note that this program only tests the visibility of the changes directly after writing the data. In principle, the Kernel is allowed to just discard your changes at any later point in time. So note that you are living on the edge here, doing changes to MAP_PRIVATE memory is not inherently safe but in practice it often works very well.

Posted by Uli Köhler in C/C++, Linux

How to get file size using boost::filesystem library

To get the filesize (in bytes) of any file (test.xml in our example) using the boost::filesystem library, use this snippet:

#include <experimental/filesystem>
#include <iostream>

using namespace std;
using namespace std::experimental::filesystem;

int main() {
    size_t filesize = file_size("test.xml");
    cout << filesize << endl;
}

You need to link the boost_filesystem and the boost_system libraries, i.e. compile like this:

g++ -o test test.cpp -lboost_filesystem -lboost_system
Posted by Uli Köhler in C/C++

How to get file size using C++17 filesystem library

To get the filesize (in bytes) of any file (test.xml in our example) using only the C++17 filesystem library, use this snippet:

New version using filesystem:

#include <filesystem>
#include <iostream>

using namespace std;
using namespace std::filesystem;

int main() {
    size_t filesize = file_size("test.xml");
    cout << filesize << endl;
}

Old version using experimental/filesystem

#include <experimental/filesystem>
#include <iostream>

using namespace std;
using namespace std::experimental::filesystem;

int main() {
    size_t filesize = file_size("test.xml");
    cout << filesize << endl;
}

With GCC/G++ you need to link the stdc++fs library, i.e. compile like this:

g++ -o test test.cpp -lstdc++fs
Posted by Uli Köhler in C/C++

How to fix GCC undefined reference to std::experimental::filesystem::…

Problem:

You want to compile an executable using GCC/G++ that uses functionality from std::experimental::filesystem but you get an error message like this during compilation / linking:

test.cpp:(.text+0x33): undefined reference to `std::experimental::filesystem::v1::file_size(std::experimental::filesystem::v1::__cxx11::path const&)'
/tmp/ccSXeCVb.o: In function `std::experimental::filesystem::v1::__cxx11::path::path<char [9], std::experimental::filesystem::v1::__cxx11::path>(char const (&) [9])':
test.cpp:(.text._ZNSt12experimental10filesystem2v17__cxx114pathC2IA9_cS3_EERKT_[_ZNSt12experimental10filesystem2v17__cxx114pathC5IA9_cS3_EERKT_]+0x73): undefined reference to `std::experimental::filesystem::v1::__cxx11::path::_M_split_cmpts()'
collect2: error: ld returned 1 exit status

Solution:

You need to link the stdc++fs library, i.e. append -lstdc++fs to your g++ command.

For example, a working command looks like this:

g++ -o myprogram main.cpp -lstdc++fs
Posted by Uli Köhler in C/C++, GCC errors