在C ++中使用不同的分配器复制对象

时间:2022-08-17 08:58:24

So I have a nice persistent allocator class persistent_alloc<T> that allows me to allocate C++ container objects and strings in persistent memory which is backed by an mmaped file that can persist from one run of my program to the next.

所以我有一个很好的持久化分配器类persistent_alloc ,它允许我在持久内存中分配C ++容器对象和字符串,该内存由一个mmaped文件支持,该文件可以从我的程序的一次运行到下一次运行。

My problem comes when I want to do anything that mixes persistent and non persistent objects. For example, I have

当我想做任何混合持久和非持久对象的事情时,我的问题就来了。例如,我有

typedef std::basic_string<char, std::char_traits<char>, persistent_alloc<char>> pstring;

pstring a, b, c;
std::string x, y, z;

I want to be able to do things like:

我希望能够做到这样的事情:

if (a == x)
    a = y;
c = z + b;

and so forth, but by default it does not work, as pstring and std::string are unrelated types. Now as far as the comparison is concerned, I can define:

依此类推,但默认情况下它不起作用,因为pstring和std :: string是不相关的类型。现在就比较而言,我可以定义:

template<typename Alloc1, typename Alloc2> inline bool
operator==(const std::basic_string<char, std::char_traits<char>, Alloc1> &a,
           const std::basic_string<char, std::char_traits<char>, Alloc2> &b)
{
    return strcmp(a.c_str(), b.c_str()) == 0;
}

...and now I can compare strings for equality. But adding these for every operation seems like a pain -- it seems like they SHOULD be provided by the standard library. Worse, assignment operators and copy constructors must be members and can't be defined as global inline functions like this.

...现在我可以比较字符串的相等性。但是为每个操作添加这些操作似乎很痛苦 - 似乎它们应该由标准库提供。更糟糕的是,赋值运算符和复制构造函数必须是成员,不能像这样定义为全局内联函数。

Is there a reasonable way of doing this? Or do I have to effectively rewrite the entire standard library to support allocators usefully?

这样做有合理的方法吗?或者我是否必须有效地重写整个标准库以有效地支持分配器?

1 个解决方案

#1


7  

There is a way to handle this, but you need to think outside of the box a bit. What you need is an intermediate type that is implicitly constructable from both std::string and your allocator string.

有一种方法可以解决这个问题,但你需要在盒子外思考一下。你需要的是一个可以从std :: string和你的分配器字符串隐式构造的中间类型。

There is a proposal for such a thing before the C++ committee currently. It is based on a Google-built Apache-licensed implementation that already exists. It's called basic_string_ref; it's a template class that is basically a pointer to the first character in a string and a size, representing the length of the string. It's not a true container in the sense that it doesn't manage memory.

目前在C ++委员会之前有一个提议。它基于Google已经存在的Apache许可实施。它叫做basic_string_ref;它是一个模板类,它基本上是指向字符串中第一个字符的指针和一个表示字符串长度的大小。它不是一个真正的容器,因为它不管理内存。

Which is exactly what you need.

这正是你需要的。

basic_string_ref for a particular character type and traits type is implicitly constructable from a std::basic_string regardless of allocator.

对于特定字符类型和特征类型的basic_string_ref可以从std :: basic_string隐式构造,而不管分配器如何。

All of the comparison operators can be defined in terms of basic_string_ref. Since it is implicitly constructable from std::basic_string (and virtually free to construct), it would work transparently for comparisons between differently allocated strings.

所有比较运算符都可以用basic_string_ref定义。由于它可以从std :: basic_string隐式构造(并且几乎可以*构造),因此它可以透明地用于不同分配的字符串之间的比较。

Doing assignment is rather trickier, but doable. It requires a series of conversions:

做任务相当棘手,但可行。它需要一系列转换:

a = pstring{basic_string_ref{y}};

Not the prettiest code, of course. We would prefer to simply change the copy constructor and assignment operator of std::basic_string to be allocator agnostic. But since that's not doable, this is really the next best thing. You could even wrap it in a template function:

当然不是最漂亮的代码。我们更愿意简单地将std :: basic_string的复制构造函数和赋值运算符更改为与分配器无关。但由于这不可行,这真的是下一个最好的事情。你甚至可以将它包装在模板函数中:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{basic_string_ref<charT, traits>{y}};
}

Of course, if you can do that, you can just do this:

当然,如果你能做到这一点,你可以这样做:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{y.begin(), y.end()};
}

It'd be great if this were just part of std::basic_string, so that you wouldn't need the workarounds. But it isn't.

如果这只是std :: basic_string的一部分会很好,所以你不需要解决方法。但事实并非如此。

#1


7  

There is a way to handle this, but you need to think outside of the box a bit. What you need is an intermediate type that is implicitly constructable from both std::string and your allocator string.

有一种方法可以解决这个问题,但你需要在盒子外思考一下。你需要的是一个可以从std :: string和你的分配器字符串隐式构造的中间类型。

There is a proposal for such a thing before the C++ committee currently. It is based on a Google-built Apache-licensed implementation that already exists. It's called basic_string_ref; it's a template class that is basically a pointer to the first character in a string and a size, representing the length of the string. It's not a true container in the sense that it doesn't manage memory.

目前在C ++委员会之前有一个提议。它基于Google已经存在的Apache许可实施。它叫做basic_string_ref;它是一个模板类,它基本上是指向字符串中第一个字符的指针和一个表示字符串长度的大小。它不是一个真正的容器,因为它不管理内存。

Which is exactly what you need.

这正是你需要的。

basic_string_ref for a particular character type and traits type is implicitly constructable from a std::basic_string regardless of allocator.

对于特定字符类型和特征类型的basic_string_ref可以从std :: basic_string隐式构造,而不管分配器如何。

All of the comparison operators can be defined in terms of basic_string_ref. Since it is implicitly constructable from std::basic_string (and virtually free to construct), it would work transparently for comparisons between differently allocated strings.

所有比较运算符都可以用basic_string_ref定义。由于它可以从std :: basic_string隐式构造(并且几乎可以*构造),因此它可以透明地用于不同分配的字符串之间的比较。

Doing assignment is rather trickier, but doable. It requires a series of conversions:

做任务相当棘手,但可行。它需要一系列转换:

a = pstring{basic_string_ref{y}};

Not the prettiest code, of course. We would prefer to simply change the copy constructor and assignment operator of std::basic_string to be allocator agnostic. But since that's not doable, this is really the next best thing. You could even wrap it in a template function:

当然不是最漂亮的代码。我们更愿意简单地将std :: basic_string的复制构造函数和赋值运算符更改为与分配器无关。但由于这不可行,这真的是下一个最好的事情。你甚至可以将它包装在模板函数中:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{basic_string_ref<charT, traits>{y}};
}

Of course, if you can do that, you can just do this:

当然,如果你能做到这一点,你可以这样做:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{y.begin(), y.end()};
}

It'd be great if this were just part of std::basic_string, so that you wouldn't need the workarounds. But it isn't.

如果这只是std :: basic_string的一部分会很好,所以你不需要解决方法。但事实并非如此。