predefined macros

OS Type

    __linux__       Defined on Linux
    __sun           Defined on Solaris
    __FreeBSD__     Defined on FreeBSD
    __NetBSD__      Defined on NetBSD
    __OpenBSD__     Defined on OpenBSD
    __APPLE__       Defined on Mac OS X
    __hpux          Defined on HP-UX
    __osf__         Defined on Tru64 UNIX (formerly DEC OSF1)
    __sgi           Defined on Irix
    _AIX            Defined on AIX
    _WIN32          Defined on Windows

usage:

#ifdef __linux__ 
    //linux code goes here
#elif _WIN32
    // windows code goes here
#else

#endif

std::bind/std::thread pass by reference

std::bind/std::thread传参数时会强制拷贝,所以传引用不会起做用,比如说:


int main() {
    int val = 5;
    std::thread thr([](int & param){
        param = 6;
    }, val);
    thr.join();
    printf("%d\n", val); // result is 5 instead of 6.
    return 0;
}

由于val在传参数拷贝了一份,在线程内修改param,并不影响val的值,所以线程退出后val的值还是5而不是6。

要解决这个问题可以用std::ref(val)这种方式去传参。std::ref(val)会把val包装在一个对象里,这个对象可以像int &一样使用(隐式转换), 但是copy的时候依然保持引用状态。下面的代码是可以达到在线程里修改val值的目的的。

int main() {
    int val = 5;
    std::thread thr([](int & param){
        param = 6;
    }, std::ref(val));
    thr.join();
    printf("%d\n", val); // result is 6
    return 0;
}

std::bind/std::thread pass by reference完美转发

std::bind/std::thread无法用std::forward实现完美转发了,需要一个helper方法:

    template <class T> std::reference_wrapper<T> maybe_wrap(T& val) { return std::ref(val); }
    template <class T> T&& maybe_wrap(T&& val) { return std::forward<T>(val); }
    
    // 转发时
    auto taskPtr = std::make_shared<packaged_task_type>(std::bind(
            std::forward<std::function<Ret(Args...)>>(callable), maybe_wrap(std::forward<Args>(args))...));
    // 即
    maybe_wrap(std::forward<Args>(args))...

C++ type list

using MyTypeList = std::tuple<GenericFile,
                                  TextFile,
                                  ImageFile,
                                  AudioFile,
                                  VideoFile>;
                                  
template<typename List, int... ints>
void UseTypes(std::integer_sequence<int, ints...> int_seq)
{
    ((printf("%d\n",ints)), ...);
    ([&]{
         printf("%d\n", ints);
         using E = typename std::tuple_element<ints, List>::type;
         auto name = E::name(); // call static member function
         auto ptr = std::make_shared<E>(); // construct an instance
         auto instName = ptr->instName(); // call instance member function
     }(),...); // fold expression
}

const int numTypes = std::tuple_size_v<MyTypeList>;
UseTypes<MyTypeList>(std::make_integer_sequence<int, numTypes>{});

C++ print variant

using Error = int;
using File = std::variant<
    Error,
    std::shared_ptr<GenericFile>,
      std::shared_ptr<TextFile>,
      std::shared_ptr<ImageFile>,
      std::shared_ptr<AudioFile>,
      std::shared_ptr<VideoFile>>;


template <typename Variant, int... ints>
void PrintResult(Variant & v, std::integer_sequence<int, ints...>) {
    ([&]{
        printf("%d\n", ints);
        using E = typename std::variant_alternative_t<ints, Variant>;
        if constexpr (std::is_same_v<E, int>) {
            if(std::holds_alternative<E>(v) { // runtime type test
                std::cout << "error " << std::get<int>(v) << std::endl;
            }
        } else {
            if(std::holds_alternative<E>(v) { // runtime type test
                std::cout << std::get<E>(v)->toString() << std::endl; // other types must implement toString
            }
        }
    }(),...);
}

auto ret = SomeFunctionThatReturnsVariant();
constexpr int numTypes = std::variant_size_v<FileInfo>;
PrintResult(ret, std::make_integer_sequence<int, numTypes>{});

static member function and compile time polymorphism

just return variant and use printResult like in the previous section.

other useful templates:

std::get_if

run time test, pass type or zero-based index, returns a pointer or nullptr.

std::variant<int, float> v;
int *pv = std::get_if<Type>(&v);
if(pv != nullptr) {
    printf("%d\n", *pv);
}
float * pf = std::get_if<1>(&v);
if(pf != nullptr) {
    printf("%f\n", *pf);
}

std::visit

include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
 
// the variant to visit
using var_t = std::variant<int, long, double, std::string>;
 
// helper constant for the visitor #3
template<class> inline constexpr bool always_false_v = false;
 
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
 
int main() {
    std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
    for (auto& v: vec) {
 
        // 1. void visitor, only called for side-effects (here, for I/O)
        std::visit([](auto&& arg){std::cout << arg;}, v);
 
        // 2. value-returning visitor, demonstrates the idiom of returning another variant
        var_t w = std::visit([](auto&& arg) -> var_t {return arg + arg;}, v);
 
        // 3. type-matching visitor: a lambda that handles each type differently
        std::cout << ". After doubling, variant holds ";
        std::visit([](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                std::cout << "int with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, long>)
                std::cout << "long with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "double with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, std::string>)
                std::cout << "std::string with value " << std::quoted(arg) << '\n';
            else 
                static_assert(always_false_v<T>, "non-exhaustive visitor!");
        }, w);
    }
 
    for (auto& v: vec) {
        // 4. another type-matching visitor: a class with 3 overloaded operator()'s
        // Note: The `(auto arg)` template operator() will bind to `int` and `long`
        //       in this case, but in its absence the `(double arg)` operator()
        //       *will also* bind to `int` and `long` because both are implicitly
        //       convertible to double. When using this form, care has to be taken
        //       that implicit conversions are handled correctly.
        std::visit(overloaded {
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
        }, v);
    }
}

simplified PrintVariant

void PrintInfo(const Variant &info) {
    std::visit([](auto && arg) {
        using T= typename std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<Error, T>) {
            FUN_INFO("error %d", arg);
        }  else {
            FUN_INFO("result: %s", arg->toString.c_str());
        }
    }, info);
}

concept

use a concept

replace typename

template <HasNameConcept T>
std::string GetName(T &t) {
    return t.name();
}

// or

template <HasStaticNameConcept T>
std::string GetName(T &t) {
    return T::name();
}

requires key word & junction

template <typename T>
requires HasNameConcept<T> && HasClassNameConcept<T>
std::string GetName(T &t) {
    return t.name() + T::class_name();
}

defination

= requires (T t) { /* compiles */}

template <typename T>
concept HasName = requires(T t) {
    t.name();
};


template <typename T>
concept HasClassName = requires(T t) {
   T::className(); 
};

static_assert(HasName<A>);
static_assert(HasClassName<A>);

alias and conjunction(compile time true/false values)

template <typename B, typename D>
concept Derived = std::is_base_of<B,D>::value;

class A {
public:
    const static int value = 8;
}
template <typename T>
concept BigClass = T::value >= 8;

template <T, D>
concept HasBothNames = HasName<A> 
                    && HasClassName<T> 
                    && (!std::is_base_of<T,D>::value)
                    && (sizeof(T) > 8 || T::class_value == 8);

partial specialization

template <typename B, typename D>
concept Derived = std::is_base_of<B,D>::value;

template <typename T>
concept DerivedFromA = Derived<A, T>;

class B :  A {};
static_assert(DerivedFromA<B>)

atomic constrains i.e. compile time bool like operator bool

can't be alias'd as a concept.

template <typename T>
struct IS {
    constexpr operator bool() {
        return T::ok;
    }
    constexpr bool func() {
        return T::ok;
    }
};

class C {
public:
    const static bool ok = true;
};

//template <typename T>
//concept XX = requires (IS<T>{});  // does not work
//
//static_assert(XX<C>); // does not work

template <typename T>
requires (IS<T>{} ) && (IS<T>{}.func())  // works
void test() {
}