C++ Code Snippet

Network

Acquire Localhost IP Address

#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/if.h>
#include <net/if_arp.h>
#include <unistd.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>

std::string ipAddressToString(uint32_t ipAddress)
{
    char buffer[INET_ADDRSTRLEN] = {0};
    uint32_t addr = htonl(ipAddress);
    return std::string(inet_ntop(AF_INET, &addr, buffer, sizeof(buffer)));
}

int main(int argc, char *argv[]) {

    int fd = socket(AF_INET, SOCK_DGRAM, 0); 
    assert(fd > 0); 

    struct ifconf ifc;
    struct ifreq *pifr;

    ifc.ifc_len = sizeof(struct ifreq) * 10; 
    ifc.ifc_buf = (char*) malloc(ifc.ifc_len);

    int err = ioctl(fd, SIOCGIFCONF, &ifc);
    assert(err >= 0); 

    int ifc_num = ifc.ifc_len / sizeof(struct ifreq);
    printf("Interfaces: %d\r\n", ifc_num);

    assert(ifc.ifc_buf != NULL);
    pifr = ifc.ifc_req;
    
    // Iterate trought all ifreq and save interface type, ip and netmask
    for (int i = 0;  i < ifc_num; ++i)
    {       
        err = ioctl(fd, SIOCGIFADDR, &pifr[i]);
        assert(err == 0); 
        uint32_t ipAddress = ntohl(reinterpret_cast<struct sockaddr_in*>(&(pifr[i].ifr_addr))->sin_addr.s_addr);

        err = ioctl(fd, SIOCGIFNETMASK, &pifr[i]);
        assert(err == 0); 
        uint32_t netmaskAddress = ntohl(reinterpret_cast<struct sockaddr_in*>(&(pifr[i].ifr_netmask))->sin_addr.s_addr);

        err = ioctl(fd, SIOCGIFBRDADDR, &pifr[i]);
        assert(err == 0); 
        uint32_t broadcastAddress = ntohl(reinterpret_cast<struct sockaddr_in*>(&(pifr[i].ifr_broadaddr))->sin_addr.s_addr);

        printf("Network Interface: %s, IP=%s, Netmask=%s, Broadcast=%s\r\n", 
               pifr[i].ifr_name, 
               ipAddressToString(ipAddress).c_str(), 
               ipAddressToString(netmaskAddress).c_str(), 
               ipAddressToString(broadcastAddress).c_str());
    }   
}

Uncategorized

22.cpp二进制形式打开文件

int begin, end;

  std::ifstream file(input_bmp_name, std::ios::in | std::ios::binary);
  if (!file) {
    LOG(FATAL) << "input file " << input_bmp_name << " not found";
    exit(-1);
  }

  begin = file.tellg();
  file.seekg(0, std::ios::end);
  end = file.tellg();
  size_t len = end - begin;

  if (s->verbose) LOG(INFO) << "len: " << len;

  std::vector<uint8_t> img_bytes(len);
  file.seekg(0, std::ios::beg);
  file.read(reinterpret_cast<char*>(img_bytes.data()), len);

21.split函数的实现

std::vector<std::string> split(const std::string &str, const std::string &sep)
{
    std::string::size_type pos;
    std::vector<std::string> result;

    std::string newstr =  str + sep;
    size_t size=newstr.size();

    for(size_t i=0; i<size; ++i)
    {
        pos=newstr.find(sep,i);
        if(pos<size)
        {
            std::string s = newstr.substr(i,pos-i);
            result.push_back(s);
            i=pos+sep.size()-1;
        }
    }
    return result;
}

20.判断是否有宏定义

#ifdef PLATFORM_QNX  
  int platform = 1;
#elif defined PLATFORM_ANDROID
  int platform = 2;
#else
  int platform = 3;
#endif

19.不支持模版别名的处理办法

__cpp_user_defined_literals测试,从gcc4.7版本开始支持模板别名,但是qnx的libc++的__config却检测到gcc就认为没有模板别名的特性

所以这里有个bug,是libc++库对gcc编译的配置选项出现了问题

参考标准库中libc++库中memory头文件中的一段代码

#ifndef _LIBCPP_HAS_NO_TEMPLATE_ALIASES
    template <class _Tp> using rebind_alloc =
                  typename __allocator_traits_rebind<allocator_type, _Tp>::type;
    template <class _Tp> using rebind_traits = allocator_traits<rebind_alloc<_Tp>>;
#else  // _LIBCPP_HAS_NO_TEMPLATE_ALIASES
    template <class _Tp> struct rebind_alloc
        {typedef typename __allocator_traits_rebind<allocator_type, _Tp>::type other;};
    template <class _Tp> struct rebind_traits
        {typedef allocator_traits<typename rebind_alloc<_Tp>::other> other;};
#endif  // _LIBCPP_HAS_NO_TEMPLATE_ALIASES

18.libstdc++/libc++

gcc配套的 libstdc++ llvm/clang++ 配套的 libc++

libstdc++包含两个库: libstdc++.so(接口层) 和 libsupc++.so(实现层) libc++包含两个库: libc++.so(接口层) libc++abi.so(实现层)

libstdc++的头文件位置 /usr/include/c++/5.4/ 其中5.4位gcc编译器的版本号 libc++头文件的位置 /usr/include/c++/v1/

配置编译选项:【注意】这个是配置选择头文件的 -stdlib=libc++ -stdlib=libstdc++

通过cmake配置

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++ -lc++abi")

17.虚析构的作用

1.通过父类指针释放所指向的子类。

class Animal {
  vitual ~Animal() {
    std::cout << "delete animal.." << std::endl;
  }
}

class Dog : public Animal {
  ~Dog() {
    std::cout << "delete dog..." << std::endl;
  };
};

Animal* animal = new Dog();

delete animal;

如上执行会输出

delete dog...
delete animal..

如果去掉了vitual修饰~Animal(),则会只输出如下

delete animal..

因为父类的构造函数和析构函数不能被子类继承,但是会形成多态,

所以当父类定义成虚析构函数的时候,会先动态调用子类的析构函数,

然后由于delete的机制,会接着调用父类的析构函数

16.待查。。。

std::atomic_bool                             shutdownFlag_;
while (!shutdownFlag_.load(std::memory_order_acquire)) {

}

15.同名的头文件

如果搜索路径中包含两个同名的头文件,这时候只会包含第一个搜索到的头文件。示例如下

目录结构

cpptest
|
|---build
|
|---include_1
|   |
|   |---same_name.hpp
|
|---include_2
|   |
|   |---same_name.hpp
|
|---main.cpp
|
|---CMakeLists.txt

include_1/same_name.hpp 中内容如下

struct cat{
  int age;
};

include_2/same_name.hpp 中内容如下

struct dog{
  int age;
};

CMakeLists.txt内容如下

project(demo)
set(CMAKE_CXX_STANDARD 11)

add_executable(
  ${PROJECT_NAME}
  main.cpp)

target_include_directories(
  ${PROJECT_NAME}
  PUBLIC
  include_2
  include_1
)

main.cpp内容如下

#include <iostream>
using namespace std;
#include "same_name.hpp"

int main(int argc, char* argv[]) {
  cat c;
  c.age = 1;
  dog d;
  d.age = 2;
  cout << d.age << endl;
}

这时候编译会报错如下

/Users/liushichao/Exercise/cpp/test_cpp/main.cpp:6:3: error: unknown type name 'cat'

调整CMakeLists.txt内容如下

project(demo)
set(CMAKE_CXX_STANDARD 11)

add_executable(
  ${PROJECT_NAME}
  main.cpp)

target_include_directories(
  ${PROJECT_NAME}
  PUBLIC
  include_1
  include_2
)

这时候编译会报错如下

/Users/liushichao/Exercise/cpp/test_cpp/main.cpp:8:3: error: unknown type name 'dog'

调整CMakeLists.txt内容如下

project(demo)
set(CMAKE_CXX_STANDARD 11)

add_executable(
  ${PROJECT_NAME}
  main.cpp)

target_include_directories(
  ${PROJECT_NAME}
  PUBLIC
)

main.cpp内容如下

#include <iostream>
using namespace std;
#include "include_1/same_name.hpp"
#include "include_2/same_name.hpp"

int main(int argc, char* argv[]) {
  cat c;
  c.age = 1;
  dog d;
  d.age = 2;
  cout << d.age << endl;

}

这样就可以运行成功了。

14.可变参数数模板

1.递归展开参数包

#include <iostream>
using namespace std;

std::string operator""_s (const char* orign) {

  return  orign;
}

template <typename T>
T sum(T head) {
  return head;
}

template <typename T, typename... Types> 
T sum(T head, Types... args) {
  cout << sizeof...(args) << endl;
  return head + sum<T>(args...);
}

int main(int argc, char* argv[]) {
  cout << sum(1, 2, 3, 4, 5) << endl;
}

13.c++操作符 / operators

c++ 11 引入了新的操作夫,用户定义字面量操作符,https://en.cppreference.com/w/cpp/language/user_literal

C++ divides the operators into the following groups:

  • Arithmetic operators
  • Assignment operators
  • Comparison operators
  • Logical operators
  • Bitwise operators

12.cpp文件中编写模板的定义

参考:

https://stackoverflow.com/a/115735

https://www.codeproject.com/Articles/48575/How-to-Define-a-Template-Class-in-a-h-File-and-Imp

//mstack.hpp
template<typename T>
class mstack{
 void push(const T& e);
};
//mstack.cpp
template<typename T>
void mstack<T>::push(const T& e) {
 data_.push_back(e);
}

//important!!!
// this func is no need to be call.
void testxxx() {
 mstack<int> ms;
 ms.push(1);
}
//client.cpp

void main() {
 mstack<int> m_c_s;
 m_c_s.push(2334);
}

11.类中的const成员函数

参考: c++ primer 5 中文版 p231

成员函数的参数列表后面紧跟的const关键字是修饰隐式的this指针的。是顶层指针(参考下边的10顶层const的介绍),表明this是指向常量对象的,const成员函数中的 this 指向的值不能修改。const 的对象上不能调用非const成员函数,所以定义成const的成员函数调用起来更灵活。

10.顶层const与底层const

参考:c++ primer 5 中文版 p57

这个是针对指针的,因为指针本身也是对象,所以const修饰指针的变量的时候可能产生歧义,一个是指针变量是常量不可变,另一种情况是指针指向的是常量,这样指针变量本身是可以修改的。

为了区分这两种情况,引入了顶层const和底层const两种类型,

 p  <--- p是指向i的指针,如果p是const的话,就叫顶层const,顶层const的写法   int* const p = i; ,const 直接修饰的p,
 
 |
 v

 i  <--- 如果i 是const的话,就是底层const, 底层const的写法 : const int * p = i; ,const 直接修饰的是int,代表指向的是常量的int类型

9.std::unordered_map 查找指定key是否存在

使用count(key)方法,如果key存在,返回1,如果不存在,返回0

8.std::map有序

std::map是有序的,但是不是插入的顺序,而是按照key的值进行排序的,默认是从小到大排序,可以通过模版参数的第三个参数指定排序类型

7.c++计时

1.包含头文件
#include <chrono>

2.计时开始位置添加
std::chrono::time_point<std::chrono::high_resolution_clock> time_begin = std::chrono::high_resolution_clock::now();

3.计时结束位置添加
long long eclipse_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - time_begin).count();
          
4.eclipse_time即为统计的时间,单位是ms

6.各种类型的最大最小值宏定义

包含头文件

#include <float.h>  //double float 需要用到这个
#include <limits.h> //int longlong long 需要用这个

下边是具体的最大最小值的宏定义

int   n1 = INT_MIN;
int   n2 = INT_MAX;
float f1 = FLT_MIN;
float f2 = FLT_MAX;
double d1 = DBL_MIN;
double d2 = DBL_MAX;
long ln1 =  LONG_MAX;
long ln2 =  LONG_MIN;
long long lln1 = LONG_LONG_MAX;
long long lln1 = LONG_LONG_MIN;

5.condition_variable

wait_for可以指定等待时间,用来实现定时器效果

下边的代码,可以通过condition.signal()来唤醒阻塞的线程,其他时候会到一秒钟自动唤醒,但是这种可能会有假唤醒的时候,如果要避免假唤醒,可以再传递一个函数,返回bool类型。

std::unique_lock<std::mutex> sleep_lock(mutex_);
condition.wait_for(sleep_lock, std::chrono::milliseconds(1000));

4.运算符优先级


*乘 /除

+加 -减

>大于 <小于

==等于 !=不等于

&&与 

||或

3.std::unique_lock 与 std::lock_guard的区别

unique_lock可以随时释放锁,调用unlock()

lock_guard需要等到生命周期结束后,才能自动释放锁。

用法示例:
    {
        std::unique_lock<std::mutex> lock_get_lane_data(mutex_lan_date_msg_);
        //lock_get_lane_data.unlock();
    }

将double转换成string时保留15位小数

#include <iomanip>
#include <sstream>

double ceshi = std::stod("3.123456789");
stringstream ss;
ss << std::setprecision(15) << ceshi;
std::string ceshi_str = ss.str();
cout << ceshi_str <<endl;

java中

其中String s=String.format("%.2f",d)表示小数点后任意两位小数,其中2为表示两位小数,若需要三位小数,把2改为3即可,其他同理。

c++ primer 5th p600

使用ms后缀

#include <chrono>
using namespace std::chrono_literals;

使用sstream输出 unsigned char存储的数字

为了节省存储空间,经常会用usigned char来存储一些数值范围比较小的数字,当用ostringstream转换成string时会出现问题,举例如下

unsinged char a = 10;
std::ostringstream oss;
oss << a;
cout << oss.str(); //输出换行

这里标准输出打印的并不是“10”,而是会换行,因为a的类型是char,所以会把a的值作为ascii码的值,转换成string,要想输出“10”,需要强转a成int类型

unsinged char a = 10;
std::ostringstream oss;
oss << (int)a;
cout << oss.str(); //输出10

OpenCV

3.本地cmake加入opencv库

find_package(OpenCV REQUIRED)  #注意大小写
target_include_directories(
  ${PROJECT_NAME}
  PUBLIC
  ${OpenCV_INCLUDE_DIRS}
)

target_link_libraries(
  ${PROJECT_NAME}
  ${OpenCV_LIBS}
)

2.编译安卓ndk库

从github下载源码,进入到源码跟目录,创建build文件夹,进入build文件夹,执行下边的编译指令

export ANDROID_NDK=/Users/liushichao/Library/Android/sdk/ndk/21.0.6113669
export ANDROID_ABI=armeabi-v7a
export ANDROID_NATIVE_API_LEVEL=android-28
export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang

cmake			       \
  -DANDROID_NDK=${ANDROID_NDK} \
  -DANDROID_ABI=${ANDROID_ABI} \
  -DANDROID_STL=c++_shared  \
  -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \
  -DBUILD_SHARED_LIBS=1 \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
  -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \
  -D BUILD_opencv_java=ON \
  -D BUILD_ANDROID_PROJECTS=ON \
        -D WITH_CUDA=OFF \
        -D WITH_MATLAB=OFF \
        -D BUILD_ANDROID_EXAMPLES=OFF \
        -D BUILD_DOCS=OFF \
        -D CMAKE_BUILD_TYPE=Release \
        -D BUILD_PERF_TESTS=OFF \
        -D BUILD_TESTS=OFF \
        -DCMAKE_INSTALL_PREFIX="/Users/liushichao/source/opencv/install_ndk" \
  ..

make -j8
make install

1.注意cv::Mat()的宽高参数

前边是高,后边是宽,不要弄反了

Mat (int rows, int cols, int type);