【问题标题】:How to make template function work for char[32] and string type?如何使模板函数适用于 char[32] 和字符串类型?
【发布时间】:2021-05-18 03:03:36
【问题描述】:

我想设计一个模板函数,它建立一个size = sizeof(T) * n的共享内存

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

函数定义如下:

#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&lt;int&gt;("a", 100, 0, 0)func&lt;double&gt;("a", 100, 0, 0.) 时没关系

但是当我打电话给func&lt;std::string&gt;("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&lt;char[32]&gt;("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&lt;char[32]&gt;("a",100, 0, "")func&lt;std::string&gt;("a", 100, 0, "") 工作?

【问题讨论】:

  • 请比“它似乎不起作用”更具体。
  • 这对于几分钟前被删除的另一个问题看起来非常熟悉,通过水晶球的普遍共识是您正在尝试分配数组。是的,看起来你是(来自std::fill)。你不能。您可以做的是创建一个简单的包装器,如struct cstring32 { char s[32]; }; 并使用它。
  • 在共享内存中存储像std::string 这样的非平凡类型将不可避免地导致未定义的行为。
  • 这段代码有问题T* ret = (T*)mmap(0, n*sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);,因为您正在为字符串分配空间,但未能构造字符串(此处未调用字符串构造函数)。如果您真的想这样做,您需要研究一种称为 placement new 的技术。该技术将允许您在分配的内存中构造对象。
  • 或者,您可以使用std::uninitialized_fill 而不是std::fill。请记住,您在这里处理的是原始内存,std::fill 不正确。

标签: c++ templates stl


【解决方案1】:

改变

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

【讨论】:

    【解决方案2】:

    首先,在函数内部分配内存并返回其指针并不是安全的做法。因为可能会忘记释放内存!

    其次,由于无法为数组分配默认值,而且所需的默认值无论如何都是空的,您可以通过 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, "");
    

    【讨论】:

    • 好的,感谢您的帮助,我想您已从之前的已关闭问题切换。我应该为之前的错误描述道歉。再次感谢
    • @xyhuang 我更新了我的答案以更接近你想要的。现在您不需要以不同的方式调用该函数!请看一看!
    猜你喜欢
    • 1970-01-01
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 2012-08-20
    • 2019-03-15
    • 2022-01-16
    • 2020-07-20
    相关资源
    最近更新 更多