如何使模板函数适用于 char[32] 和字符串类型?

How to make template function work for char[32] and string type?

我想设计一个模板函数,构建一个大小为 sizeof(T) * n 的共享内存

它returns模板类型指针。我将默认值作为默认值传递。

函数定义看起来像:

#ifndef SHMHELP_HPP_                                                                                                                                                                                                                                                          
#define SHMHELP_HPP_
          
#include <bits/stdc++.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
        
namespace cppbox {
  namespace shm {
    enum EmShmOpenMode:int {
      MODE_CREATE,
      MODE_RD,
    };

    template<typename T>  //, T v>
    T* func(const char* filename, size_t n, int rdflag, T v) {  // mode: 0666
      int offlag = rdflag == EmShmOpenMode::MODE_CREATE ? O_CREAT | O_EXCL | O_RDWR :  offlag = O_RDWR;
      int shm_fd = shm_open(filename, offlag, 0666);
      if (-1 == shm_fd)  {
        if (rdflag != EmShmOpenMode::MODE_CREATE) {
          std::cerr << "shm_open open failed: " << strerror(errno) << std::endl;
          return nullptr;
        }
        offlag = O_RDWR| O_TRUNC;
        if (-1 == (shm_fd = shm_open(filename, offlag, 0666))) {
          std::cerr << "shm_open create failed: " << strerror(errno) << std::endl;
          return nullptr;
        }
      }

      if (rdflag == EmShmOpenMode::MODE_CREATE) {
        if (ftruncate(shm_fd, n*sizeof(T))) {
          std::cerr << "ftruncate failed: " << strerror(errno) << std::endl;
          close(shm_fd);
          return nullptr;
        }
      }

      T* ret = (T*)mmap(0, n*sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
      close(shm_fd);
      if (ret == MAP_FAILED) {
        std::cerr << "mmap failed: " << strerror(errno) << std::endl;
        return nullptr;
      }

      if (rdflag == EmShmOpenMode::MODE_CREATE) std::fill((T*)ret, ((T*)ret) + n, v);
      return ret;
    }
  }
};
#endif  // SHMHELP_HPP_

当我调用func<int>("a", 100, 0, 0)func<double>("a", 100, 0, 0.)

时没问题

但是当我调用 func<std::string>("a", 100, 0, "")

时它崩溃了
int main() {
  std::string*p = cppbox::shm::MapShm<std::string>("b", 100, cppbox::shm::MODE_CREATE, "huang");
  for (int i = 0; i < 100; ++i) {
    cout << (*p)[i] << " ";
  }
} 

当我像这样调用 func<char[32]>("a", 100, 0, "") 时,编译器将拒绝编译:

int main() {
    char[32]*p = cppbox::shm::MapShm<char[32]>("b", 100, cppbox::shm::MODE_CREATE, "huang");  // compiler will reject in this line
    for (int i = 0; i < 100; ++i) {
      cout << (*p)[i] << " ";        
    }//*/
  }

如何让 func<char[32]>("a",100, 0, "")func<std::string>("a", 100, 0, "") 工作?

改变

if (rdflag == EmShmOpenMode::MODE_CREATE) std::fill((T*)ret, ((T*)ret) + n, v);

if (rdflag == EmShmOpenMode::MODE_CREATE) std::uninitialized_fill((T*)ret, ((T*)ret) + n, v);

这应该有助于解决 std::string 案例。

std::fill 只能用于已经包含对象的内存。在您的情况下,您有未初始化的(原始)内存,不包含任何构造的对象,因此应该使用 std::uninitialized_fill 代替。

首先,在函数内部分配内存和 return 它的指针是不安全的做法。因为一个人可能会忘记释放内存!

其次,因为无法为数组分配默认值,并且所需的默认值无论如何都是空的,您可以通过 2 个函数重载来实现,一个用于分配默认值,另一个用于分配内存喜欢:

#include <stdio.h>
#include <type_traits>


//general purpose type that can be constructed from any type!
class all{
public:
    template<typename T>
    all(T){}
};

//First overload
template <typename T>
T* pre_func(size_t n, T defaultvalue) {
  T *pointer = (T*) calloc(n, sizeof(T));
  for(size_t counter = n; counter--;){
      pointer[counter] = defaultvalue;
  }
  return pointer;
}

//second overload
template <typename T>
T* pre_func(size_t n) {
  T *pointer = (T*) calloc(n, sizeof(T));
  return pointer;
}

//overload selector
template <typename T>
T* func(size_t n, typename std::conditional<std::is_array<T>::value, all, T>::type defaultvalue){
    if constexpr(std::is_array<T>::value){
        return pre_func<T>(n);
    }else{
        return pre_func<T>(n, defaultvalue);
    }
}

然后在您的程序中调用函数以任何您想要的方式重载普通类型和数组类型,例如:

    func<int>(100, 0);
    func<char[32]>(100, "");