围绕基本类型的c++类包装器

时间:2021-12-22 22:27:34

Many libraries I have seen/used have typedefs to provide portable, fixed size variables, eg int8, uint8, int16, uint16, etc which will be the correct size regardless of platform (and c++11 does it itself with the header stdint.h)

我所看到/使用的许多库都有typedef来提供可移植的、固定大小的变量,如int8、uint8、int16、uint16等,无论使用什么平台,这些库的大小都是正确的(而c++11使用header stdint.h自动完成)

After recently using binary file i/o in a small library I'm writing I can see the benefit of using typedefs in this way to ensure the code is portable.

在最近在我正在编写的一个小库中使用二进制文件i/o之后,我可以看到以这种方式使用typedefs以确保代码是可移植的。

However, if I'm going to the trouble of typing "namespace::uint32" rather than using built in fundamental types, I may as well make the replacement as useful as possible. Therefore I am considering using classes instead of simple typedefs.

但是,如果我要麻烦地输入“namespace::uint32”,而不是在基本类型中使用内置类型,那么我最好尽可能地使用替换。因此,我正在考虑使用类而不是简单的typedef。

These wrapper classes would implement all normal operators so could be used interchangeably with the fundamental type.

这些包装器类将实现所有正常的操作符,因此可以与基本类型互换使用。

Eg:

例如:

int x = 0;
//do stuff

could become

可能会成为

class intWrapper {
//whatever
};

intWrapper = 0;
//do stuff

without having to modify any code in "//do stuff"

无需修改“//do stuff”中的任何代码

The reason I'm considering this approach as opposed to just typedefs is the fact I already have functions that operate on fundamental types, eg

我之所以考虑使用这种方法而不只是使用typedef是因为我已经有了对基本类型进行操作的函数,例如

std::string numberToString(double toConvert);

std::string numberToHexString(double toConvert);

int intToXSignificantPlaces(const int& number, 
                               unsigned char numberOfSignificantPlaces);

bool numbersAreApproximatelyEqual(float tollerance);
//etc....

Syntactically it would be nicer (and more oop) to do the following:

从句法上来说,做以下事情会更好(也更符合oop):

intWrapper.toString();
intWrapper.toHexString();
//etc

Also it would allow me to implement bigint classes (int128, etc) and have those and the smaller ones (based on fundamental types) use identical interfaces.

它还允许我实现bigint类(int128等),并让它们和较小的类(基于基本类型)使用相同的接口。

Finally each wrapper could have a static instance of itself called max and min, so the nice syntax of int32::max and int32::min would be possible.

最后,每个包装器都可以有一个名为max和min的静态实例,因此可以使用int32::max和int32::min的漂亮语法。

However, I have a few concerns that I would like to address before doing this (since it is mostly syntactical sugar and these types would be used so commonly any extra overhead could have a significant performance impact).

然而,在进行此操作之前,我有一些需要注意的问题(因为它主要是语法上的糖,而且这些类型通常会被使用,所以任何额外的开销都可能对性能产生重大影响)。

1) Is there any additional function calling overhead when using someClass.operator+(), someClass.operator-() etc over just int a + int b? If so, would inlining operator+() eliminate ALL this overhead?

1)在使用someClass.operator+()、someClass.operator-()和int a + int b时,是否有其他函数调用开销?如果是,内联操作符+()会消除所有这些开销吗?

2) All external functions require the primitive type, eg glVertex3f(float, float, float) could not simply be passed 3 floatWrapper objects, is there a way to automatically make the compiler cast the floatWrapper to a float? If so, are there performance impacts?

2)所有外部函数都需要基元类型,例如glVertex3f(float, float, float)不能简单地传递3个floatWrapper对象,有没有一种方法可以自动让编译器将floatWrapper转换成float?如果是,是否存在性能影响?

3) Is there any additional memory overhead? I understand(?) that classes with inheritance have some sort of virtual table pointer and so use slightly more memory (or is that just for virtual functions?), but assuming these wrapper classes are not inherited from/are not child classes there isn't any additional memory use using classes instead of fundamental types, is there?

3)是否有额外的内存开销?我理解(?)类和继承有某种虚拟表指针,所以使用稍微更多的内存(或只是虚函数吗?),但是如果这些包装类不是继承/不是子类没有任何额外的内存使用使用类而不是基本类型,是吗?

4) Are there any other problems / performance impacts this could cause?

4)是否还有其他问题/性能影响?

3 个解决方案

#1


10  

1) Is there any additional function calling overhead when using someClass.operator+()

1)在使用someClass.operator+()时是否有额外的函数调用开销

No, if the function body is small and in the header, it will be inlined, and have no overhead

不,如果函数体很小,并且在header中,它将是内联的,并且没有开销

2) Is there a way to automatically make the compiler cast the floatWrapper to a float?

是否有一种方法可以自动使编译器将floatWrapper转换为浮点数?

struct floatWrapper {
    floatWrapper(float); //implicit conversion from float
    operator float(); //implicit conversion to float.  
};

Again, if the body of the function is small and in the header, it will be inlined, and have no overhead

同样,如果函数的主体很小,并且在页眉中,它将是内联的,并且没有开销

3) Is there any additional memory overhead?

3)是否有额外的内存开销?

not if there's no virtual functions. A class is called polymorphic if it declares or inherits any virtual functions. If a class is not polymorphic, the objects do not need to include a pointer to a virtual function table. Moreover, performing dynamic_cast of a pointer/reference to a non-polymorphic class down the inheritance hierarchy to a pointer/reference to a derived class is not allowed, so there is no need for the objects to have some kind of type information.

如果没有虚函数就不行。如果类声明或继承任何虚函数,则称为多态类。如果类不是多态性的,则对象不需要包含指向虚函数表的指针。此外,不允许在继承层次结构中执行指向非多态性类的指针/引用的dynamic_cast到派生类的指针/引用,因此不需要对象具有某种类型信息。

4) Are there any other problems / performance impacts this could cause?

4)是否还有其他问题/性能影响?

performance? No.

性能?不。

Also, be sure to implement binary operators that don't modify the lhs as free functions, and overload them to support all relevant permutations of floatWrapper and float.

另外,一定要实现不将lhs作为*函数修改的二进制操作符,并将其重载以支持floatWrapper和float的所有相关排列。

struct floatWrapper {
    explicit floatWrapper(float);
    operator float(); //implicit conversion to float.  
    floatWrapper operator-=(float);
};
floatWrapper operator-(floatWrapper lhs, floatWrapper rhs) 
{return lhs-=rhs;}
floatWrapper operator-(float lhs, floatWrapper rhs) 
{return floatWrapper(lhs)-=rhs;}
floatWrapper operator-(floatWrapper lhs, float rhs) 
{return lhs-=rhs;}

Here's my attempt at such a thing. Note you'll need a slightly different version for float/double/long double.

这是我对这种事的尝试。注意,对于float/double/long double,您需要一个稍微不同的版本。

#2


2  

It depends on the compiler. If it has loops or allocations, less likely to be inlined.

这取决于编译器。如果它有循环或分配,就不太可能内联。

#3


0  

I think the answers are not completely correct - at least for gcc 4 I have observed a significant overhead due to the constructor and operator calls.

我认为答案并不完全正确——至少对于gcc 4来说,我观察到由于构造函数和操作符调用而造成的巨大开销。

The following takes about twice as long as with long:

下面这段视频的时长是长视频的两倍:

typedef intWrapperImpl<long> TestWrapper;
//typedef long TestWrapper;

int main (int argc, char** argv) {
    TestWrapper sum(0);
    TestWrapper test(4);

    for (long i = 0; i < 1000000000L; ++i) {
        sum += test;
    }

    cout << sum << "\n";

    return 0;
}

Using different versions of gcc 4 with and with out optimization led to no differences in the performance.

使用不同版本的gcc 4进行带和不带优化不会导致性能上的差异。

In this case, adding

在这种情况下,添加

intWrapperImpl& operator+=(const intWrapperImpl & v) {value+=v.value; return *this;}

gives only a slight improvement.

只有轻微的改善。

Using such a wrapper as if they were base-types in performance critical code seems to be a bad idea. Using them locally and thereby invoking the constructor all the time seems even worse.

使用这样的包装器,就好像它们是基本类型的性能关键代码似乎是个坏主意。在本地使用它们并因此一直调用构造函数似乎更糟糕。

This really came as a surprise to me, since it should easily be possible to inline everything and optimize it as if it would be a base-type variable.

这确实让我感到惊讶,因为它应该可以很容易地内联所有内容并对其进行优化,就好像它是一个基本类型的变量一样。

Any further hints would be greatly appreciated!

如有任何进一步的提示,我们将不胜感激!

#1


10  

1) Is there any additional function calling overhead when using someClass.operator+()

1)在使用someClass.operator+()时是否有额外的函数调用开销

No, if the function body is small and in the header, it will be inlined, and have no overhead

不,如果函数体很小,并且在header中,它将是内联的,并且没有开销

2) Is there a way to automatically make the compiler cast the floatWrapper to a float?

是否有一种方法可以自动使编译器将floatWrapper转换为浮点数?

struct floatWrapper {
    floatWrapper(float); //implicit conversion from float
    operator float(); //implicit conversion to float.  
};

Again, if the body of the function is small and in the header, it will be inlined, and have no overhead

同样,如果函数的主体很小,并且在页眉中,它将是内联的,并且没有开销

3) Is there any additional memory overhead?

3)是否有额外的内存开销?

not if there's no virtual functions. A class is called polymorphic if it declares or inherits any virtual functions. If a class is not polymorphic, the objects do not need to include a pointer to a virtual function table. Moreover, performing dynamic_cast of a pointer/reference to a non-polymorphic class down the inheritance hierarchy to a pointer/reference to a derived class is not allowed, so there is no need for the objects to have some kind of type information.

如果没有虚函数就不行。如果类声明或继承任何虚函数,则称为多态类。如果类不是多态性的,则对象不需要包含指向虚函数表的指针。此外,不允许在继承层次结构中执行指向非多态性类的指针/引用的dynamic_cast到派生类的指针/引用,因此不需要对象具有某种类型信息。

4) Are there any other problems / performance impacts this could cause?

4)是否还有其他问题/性能影响?

performance? No.

性能?不。

Also, be sure to implement binary operators that don't modify the lhs as free functions, and overload them to support all relevant permutations of floatWrapper and float.

另外,一定要实现不将lhs作为*函数修改的二进制操作符,并将其重载以支持floatWrapper和float的所有相关排列。

struct floatWrapper {
    explicit floatWrapper(float);
    operator float(); //implicit conversion to float.  
    floatWrapper operator-=(float);
};
floatWrapper operator-(floatWrapper lhs, floatWrapper rhs) 
{return lhs-=rhs;}
floatWrapper operator-(float lhs, floatWrapper rhs) 
{return floatWrapper(lhs)-=rhs;}
floatWrapper operator-(floatWrapper lhs, float rhs) 
{return lhs-=rhs;}

Here's my attempt at such a thing. Note you'll need a slightly different version for float/double/long double.

这是我对这种事的尝试。注意,对于float/double/long double,您需要一个稍微不同的版本。

#2


2  

It depends on the compiler. If it has loops or allocations, less likely to be inlined.

这取决于编译器。如果它有循环或分配,就不太可能内联。

#3


0  

I think the answers are not completely correct - at least for gcc 4 I have observed a significant overhead due to the constructor and operator calls.

我认为答案并不完全正确——至少对于gcc 4来说,我观察到由于构造函数和操作符调用而造成的巨大开销。

The following takes about twice as long as with long:

下面这段视频的时长是长视频的两倍:

typedef intWrapperImpl<long> TestWrapper;
//typedef long TestWrapper;

int main (int argc, char** argv) {
    TestWrapper sum(0);
    TestWrapper test(4);

    for (long i = 0; i < 1000000000L; ++i) {
        sum += test;
    }

    cout << sum << "\n";

    return 0;
}

Using different versions of gcc 4 with and with out optimization led to no differences in the performance.

使用不同版本的gcc 4进行带和不带优化不会导致性能上的差异。

In this case, adding

在这种情况下,添加

intWrapperImpl& operator+=(const intWrapperImpl & v) {value+=v.value; return *this;}

gives only a slight improvement.

只有轻微的改善。

Using such a wrapper as if they were base-types in performance critical code seems to be a bad idea. Using them locally and thereby invoking the constructor all the time seems even worse.

使用这样的包装器,就好像它们是基本类型的性能关键代码似乎是个坏主意。在本地使用它们并因此一直调用构造函数似乎更糟糕。

This really came as a surprise to me, since it should easily be possible to inline everything and optimize it as if it would be a base-type variable.

这确实让我感到惊讶,因为它应该可以很容易地内联所有内容并对其进行优化,就好像它是一个基本类型的变量一样。

Any further hints would be greatly appreciated!

如有任何进一步的提示,我们将不胜感激!