The static cast performs conversions between compatible types. It is similar to the C-style cast, but is more restrictive. For example, the C-style cast would allow an integer pointer to point to a char.
char c = 10; // 1 byte
int *p = (int*)&c; // 4 bytes
Since this results in a 4-byte pointer pointing to 1 byte of allocated memory, writing to this pointer will either cause a run-time error or will overwrite some adjacent memory.
*p = 5; // run-time error: stack corruption
In contrast to the C-style cast, the static cast will allow the compiler to check that the pointer and pointee data types are compatible, which allows the programmer to catch this incorrect pointer assignment during compilation.
int *q = static_cast<int*>(&c); // compile-time error
// example in a templated class
typedef pcl::PointCloud<PointInT> PointCloudIn;
typedef typename PointCloudIn::Ptr PointCloudInPtr; // need typename due to Ptr
typedef typename PointCloudIn::ConstPtr PointCloudInConstPtr; // need typename
// example in a templated function
template <typename PointT> // needed for templated argument type dependent on PointT
void func(typename pcl::PointCloud<PointT>::Ptr& cloud){
// if creating a new instance dependent on PointT
typename pcl::KdTreeFLANN<PointT>::Ptr tree;
}
// example error message without typename keyword
//// error: need ‘typename’ before ‘pcl::KdTreeFLANN<PointT>::Ptr’
//// because ‘pcl::KdTreeFLANN<PointT>’ is a dependent scope
// one more example
template <typename FeatureT>
class MyPerception
{
public:
using PointT = pcl::PointXYZRGBNormal;
using NormalT = pcl::PointXYZRGBNormal;
using PointCloudPtr = pcl::PointCloud<PointT>::Ptr; // not needed
using KeypointPtr = pcl::PointCloud<PointT>::Ptr; // not needed
using FeaturePtr = typename pcl::PointCloud<FeatureT>::Ptr; // needed
};
template keyword is needed to tell the compiler explicitly that .cast is a template
// In PCL gicp.hpp
// I would like to save the transformation matrix in each iteration for visualization
rigid_transformation_estimation_(output, source_indices, *target_, target_indices, transformation_);
transformations_.push_back(transformation_.template cast<double>());
class MyClass {
public:
MyClass (): point(0.1) {};
typedef boost::shared_ptr<MyClass> Ptr;
typedef boost::shared_ptr<const MyClass> ConstPtr;
void mem_func (MyClass::Ptr ptr) const;
float point;
};
// not allowed to change the member variables of MyClass::Ptr (which is the raw pointer)
void func(const MyClass::Ptr ptr){
ptr->point = 1.0; // OK
ptr.reset(); // error: not able to reset to null or to other instance
}
// not allowed to change the member variables of MyClass
void func(MyClass::ConstPtr ptr){
ptr->point = 1.0; // error: not able to change the value
ptr.reset(); // OK
}
void MyClass::mem_func(MyClass::Ptr ptr) const {
point = 1.0; // error: not allowed to change member variables
this->point = 1.0; // error: not allowed to change member variables
ptr->point = 1.0; // OK (external pointer)
}
mutable
Recall that the const reference or pointer is constrained to
only read access for any visible data members,
permission to call only methods that are marked as const (because non-const member functions can write to data members, which breaks the fence).
The mutable keyword can allow you to modify a variable in a const method. It is like making exceptions for a few special variables.
pcl::PointCloud<PointT> cloud; // instance
pcl::PointCloud<PointT>::Ptr cloud_ptr (new pcl::PointCloud<PointT>); //smart pointer
auto cloud_raw_ptr = new pcl::PointCloud<PointT>; // raw pointer
*cloud_ptr // instance
*cloud_raw_ptr // instance typeid(*cloud_ptr) == typeid(*cloud_raw_ptr)
&cloud // raw pointer
&(*cloud_ptr) // will turn the smart pointer into a raw pointer
std::cout << typeid(*cloud_ptr).name() << std::endl;
iterator
There are five types of iterator available in STL.
Bidirectional Iterator available for std::list,std::set and std::map
Random Access Iterator available for std::vector and std::deque
No iterator available for std::stack, std::queue and std::priority_queue
std::vector<int> vec{1, 2, 3, 4, 5};
std::vector<int>::iterator it;
for (it = vec.begin(); it < vec.end(); ++it)
std::cout << *it << "\n";
// begin() points to the first element in the container
// end() points to the element after the last element (empty)
function pointer
// in keypoint.h
typedef boost::function<int (int, double, std::vector<int> &, std::vector<float> &)> SearchMethod;
// class Search in search.h
virtual int
radiusSearch (int index, double radius, std::vector<int> &k_indices,
std::vector<float> &k_sqr_distances, unsigned int max_nn = 0) const;
// in pcl::Keypoint<PointInT, PointOutT>::initCompute ()
int (KdTree::*radiusSearch)(int index, double radius, std::vector<int> &k_indices,
std::vector<float> &k_distances, unsigned int max_nn) const = &KdTree::radiusSearch;
search_method_ = boost::bind (radiusSearch, boost::ref (tree_), _1, _2, _3, _4, 0);
// boost::ref is useful for passing references to function templates (algorithms)
// that would usually take copies (values) of their arguments.
// the simplest function pointer
void (*fun_ptr)(int) = &fun; // fun_ptr is a pointer to function fun()
Enum: helps to assign constants to a set of names to make program easier to read, maintain and understand
Enums are not actual variables; they're just a semi-type-safe form of #define. They're a way of storing a number in a reader-friendly format. The compiler will transform all uses of an enumerator into the actual numerical value.
Union: helps to store data of different types as a single unit
// Enum example in OpenCV (traits.hpp)
// template<> class DataType<bool>
enum { generic_type = 0,
depth = CV_8U,
channels = 1,
fmt = (int)'u',
type = CV_MAKETYPE(depth, channels)
};
// Enum example in point cloud registration
enum class RegistrationMethod { PCL, MTPCL, FAST };
struct RegistrationResult {
RegistrationMethod method;
double time_elapsed;
double fitness;
Eigen::Matrix4d transformation;
};
// Union example in PCL (point_types.hpp)
// PointXYZLAB
union
{
struct
{
float L;
float a;
float b;
};
float data_lab[4];
};
For returning two values we can use a std::pair (usually typedef'd). In C++11 and newer, there's std::tuple for more than two return results. With introduction of structured binding in C++17, returning std::tuple should probably become accepted standard.
In practice, if multiple result values are needed, the best practice is to create a struct to store these values and return this struct or change it as the pass-by-reference argument.
It is a keyword to tell compiler that this variable may be modified from outside the program which compiler is not aware of. Therefore, please do not optimize this variable.
It is a qualifier that can also be applied to methods/functions.
volatile and const are two faces of the same coin.
// Example without volatile
int some_int = 100;
while (some_int == 100) { // compilier may optimize this to while(true)
// code not related to some_int
}
// Example with volatile
volatile int some_int = 100; // tells compiler not to optimize this variable
while (some_int == 100) {
// code not related to some_int
}
This refers to a set of instructions to synchronize memory access (read/writes occur in the order you expect). For example a 'full fence' means all read/writes before the fence are comitted before those after the fence. It is a hardware level concept, and in higher level languages we are used to dealing with mutexes and semaphores.
Note that the keyword volatiledoes not guarantee a memory barrier to enforce cache-consistency. Although It is guaranteed that volatile reads/writes will happen in the exact order specified in the source code, but volatile reads/writes can still be reordered with respect to non-volatile ones. (This is referred to as compiler reordering optimizations.)
Thread #1 Core #1:
while (f == 0);
// Memory fence required here
print x;
By default, std::pair and std::tuple are not hashable, and therefore they cannot serve as keys in std::unordered_map. For hashing and combining multiple values, one way is to use boost::hash_combine function. Alternatively, we can write our own hash functions.
/**
* @brief Templated hash function for Eigen::Matrix class.
*
* Example usage:
* std::unordered_map<Eigen::Vector3i, double, hash_matrix<Eigen::Vector3i>>
*
* @note This implementation is oblivious to the storage order of Eigen matrix
* (column- or row-major). It will give you the same hash value for two different
* matrices if they are the transpose of each other in different storage order.
*
* The code is from `hash_combine` function of the Boost library.
*/
template<typename T>
struct hash_matrix : std::unary_function<T, std::size_t> {
std::size_t operator()(T const& matrix) const {
size_t seed = 0;
for (size_t i = 0; i < matrix.size(); ++i) {
auto elem = *(matrix.data() + i);
seed ^= std::hash<typename T::Scalar>()(elem) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
return seed;
}
};
/**
* @brief An alternative hash function optimized to reduce collision rate for
* numerous vectors that all contain elements from a small continuous distribution.
*/
std::size_t operator()(std::vector<uint32_t> const& vec) const {
std::size_t seed = vec.size();
for(auto x : vec) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
return seed;
}
pcl::Registration<PointT, PointT>::Ptr base_ptr;
if (method == "pcl")
base_ptr = std::make_shared<pcl::GeneralizedICP<PointT, PointT>>();
else if (method == "pclmt")
base_ptr = std::make_shared<pcl::MultithreadedGeneralizedICP<PointT, PointT>>();
else if (method == "fast")
base_ptr = std::make_shared<fast_gicp::FastGICP<PointT, PointT>>();
else
throw std::runtime_error("unknown gicp method");
// Then use base_ptr to call functions in derived class
// Note: these functions must already exist in base class as virtual functions
base_ptr->setInputSource(source);
base_ptr->setInputTarget(target);
factory method
The most basic factory method
unique_ptr<Animal> makeAnimal(const string& type, int number) {
if (type == "Dog") return make_unique<Dog>(number);
if (type == "Cat") return make_unique<Cat>(number);
throw runtime_error("Invalid type!")
}