Let's say I want to make some sort of engine which should support loading graphical Image
s, so I have
假设我想制作一些应该支持加载图形图像的引擎,所以我有
struct Image;
Image* load_image_from_file(...);
I don't want external world to know what Image
really is, they'll deal only with pointers to it.
我不希望外部世界知道Image究竟是什么,他们只会处理指向它的指针。
However inside engine
I want to use specific type, e.g. SDL_Surface
which is fully defined in SDL.
但是在引擎内部我想使用特定类型,例如SDL_Surface,在SDL中完全定义。
Can I somehow redifine Image for this file so compiler assumes SDL_Surface*
each time it sees Image*
(other than macro)?
我可以以某种方式重新映射此文件的图像,因此编译器每次看到Image *(除宏之外)都假设SDL_Surface *?
I.e. I want something like typedef struct SDL_Surface Image;
即我想要类似typedef struct SDL_Surface Image;
All attempts like
所有尝试都喜欢
using Image = SDL_Surface;
typedef SDL_Surface Image;
typedef struct SDL_Surface Image;
produce compile time error (http://codepad.org/1cFn18oh).
产生编译时错误(http://codepad.org/1cFn18oh)。
I know that I can use something like struct Image{SDL_Surface* surface};
in engine.c
/engine.cpp
but it creates unnecessary indirection and I'll have to type ->surface
. Another dirty solution is to use explicit casts, e.g.((SDL_Surface*)image)
but I'm interesting in cleaner renaming.
我知道我可以使用像struct Image {SDL_Surface * surface};在engine.c / engine.cpp但它创建了不必要的间接,我将不得不输入 - > surface。另一个肮脏的解决方案是使用显式强制转换,例如((SDL_Surface *)图像)但我更清楚重命名。
PS. I'm interested in answers for both C and C++.
PS。我对C和C ++的答案感兴趣。
6 个解决方案
#1
Simply define an alias:
只需定义一个别名:
using Image = SDL_Surface;
typedef SDL_Surface Image;
which compiles just fine.
编译得很好。
If you need to hide SDL_Surface
, just import it into some anonymous or detail
-named namespace and use it like this.
如果你需要隐藏SDL_Surface,只需将它导入一些匿名或详细命名的命名空间,并像这样使用它。
If, for some reasons, you want to define your own Image
type, you can always declare a(n) (implicit) conversion function/operator, like:
如果由于某些原因,您想要定义自己的Image类型,则可以始终声明一个(n)(隐式)转换函数/运算符,如:
struct Image {
/* explicit */ operator SDL_Surface() const;
// ...
};
and also back to Image
, if you need that:
如果你需要,还可以回到Image:
struct Image {
/* explicit */ Image(SDL_Surface&&);
/* explicit */ Image(SDL_Surface const&);
// ...
};
#2
In C++ you can use the inheritance:
在C ++中,您可以使用继承:
// User view
struct Image; // forward declaration (inclomplete type).
Image* LoadFromFile (...); // You can use pointer to incomplete type
// Implementation view
struct Image: SDL_Surface { }; // here you go !! :-)
Remark: it would be safer to use classes and private inheritance, so that only Image knows that it is an SDL_Surface.
备注:使用类和私有继承会更安全,因此只有Image知道它是SDL_Surface。
In some cases it could be undesirable to to inherit from an existing implementation class (for example if you'd need a virtual destructor and the base class doesn't). Then the PIMPL idiom could be an alternative (at the cost of an additional indirection):
在某些情况下,从现有的实现类继承可能是不可取的(例如,如果您需要虚拟析构函数而基类不需要)。那么PIMPL习惯用法可能是另一种选择(以额外的间接为代价):
//User View unchanged
struct Image;
int TestImage(Image*z);
//implementation view
struct Image {
struct ImageImpl { int x; }; // nested definition or typedef or whatever
ImageImpl *p; // works in every case, at cost of an extra indirection instead of a pointer
};
int TestImage(Image* z)
{
return z->p->x;
}
The main advantage of PIMPL here, is that you could expose more than just an incomplete type, and hence offer to the clients some useful member functions. But if you don't need this, and as you already work with poitners to the object on the client side, you could as well go directly to composition and have an ImageImpl
member instead of a PIMPL pointer.
PIMPL的主要优点在于,您可以暴露的不仅仅是不完整的类型,因此为客户提供了一些有用的成员函数。但是如果你不需要这个,并且因为你已经与poitners一起使用客户端的对象,你也可以直接进入组合并拥有一个ImageImpl成员而不是PIMPL指针。
In C, you can't use inheritance. But composition would certainly do the trick:
在C中,您不能使用继承。但组成肯定会有所作为:
struct Image {
SDL_Surface s;
};
#3
Such operations are normally done with PIMPL (pointer to implementation) pattern. But if you want to avoid indirections for now, or if the type is incomplete (this isn't the case with SDL_Surface
, but it is with many other SDL classes) you can use pointer to void
, since it can point to any data, and then cast it on the implementation side.
这些操作通常使用PIMPL(指向实现的指针)模式完成。但是如果你现在想要避免间接,或者类型是不完整的(SDL_Surface不是这种情况,但是它与许多其他SDL类一样)你可以使用指向void的指针,因为它可以指向任何数据,然后将其投射到实现方面。
Here, we use std::unique_ptr
to make use of Rule of Zero. Such Image
is now non-copyable, but movable. If you want to be able to copy it, use a value_ptr
-like pointer (not in the standard, but you can easily write such pointer yourself or use a third-party one)
在这里,我们使用std :: unique_ptr来使用Zero of Zero。这样的图像现在是不可复制的,但可移动。如果你想复制它,请使用类似value_ptr的指针(不在标准中,但你可以自己轻松编写这样的指针或使用第三方指针)
#include <memory>
struct ImageDeleter
{
void operator()(void* ptr) const;
};
class Image
{
public: // but don't touch it
std::unique_ptr<void, ImageDeleter> internal;
private:
/* private operations on surface */
public:
/* public operations */
void save(const std::string& path) const;
Image(int width, int height);
};
// EXAMPLE USAGE
// Image img(640, 480);
// img.save("aaa.bmp");
// IN THE DEFINITION FILE
#include <SDL2/SDL.h>
namespace detail
{
SDL_Surface* as_surface(const Image& img)
{
return static_cast<SDL_Surface*>(img.internal.get());
}
}
void ImageDeleter::operator()(void* ptr) const
{
SDL_FreeSurface(static_cast<SDL_Surface*>(ptr));
}
Image::Image(int width, int height) :
internal(SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0))
{
}
void Image::save(const std::string& path) const
{
SDL_SaveBMP(detail::as_surface(*this), path.c_str());
}
#4
If your client code doesn't do anything with the image, other than pass a pointer to it, you can use the Windows API trick:
如果您的客户端代码对图像没有任何作用,除了传递指针之外,您可以使用Windows API技巧:
typedef void *HIMAGE; // Image Handle
HIMAGE LoadImage(...);
#5
In C you can resort to an incomplete type.
在C中,您可以采用不完整的类型。
So you define your API in a header file:
因此,您在头文件中定义API:
myapi.h
struct Image;
struct Image* load_image_from_file(...);
...
Note that your struct Image
type, though available for your clients, is completely hidden from them.
请注意,您的结构图像类型虽然可供您的客户使用,但却完全对它们隐藏。
Now your implementation does the full declaration of your struct:
现在您的实现完成了struct的完整声明:
myapi.c:
struct Image {
/* whatever you want to put here */
/* even SDL_Surface */
/* or pointer to it */
};
/* function bodies */
You bundle the compiled C code (object, static or dynamic library) and the header to your clients.
您将已编译的C代码(对象,静态或动态库)和标头捆绑到您的客户端。
#6
In the header you export you can forward SDL_Surface
and then declare Image
to be a pointer to it. Like:
在导出的标题中,您可以转发SDL_Surface,然后将Image声明为指向它的指针。喜欢:
struct SDL_Surface;
typedef SDL_Surface* Image;
extern Image load_image_from_file(char*);
This way your library can be used without the SDL headers.
However, SDL.dll is still needed.
这样,您的库可以在没有SDL标头的情况下使用。但是,仍然需要SDL.dll。
#1
Simply define an alias:
只需定义一个别名:
using Image = SDL_Surface;
typedef SDL_Surface Image;
which compiles just fine.
编译得很好。
If you need to hide SDL_Surface
, just import it into some anonymous or detail
-named namespace and use it like this.
如果你需要隐藏SDL_Surface,只需将它导入一些匿名或详细命名的命名空间,并像这样使用它。
If, for some reasons, you want to define your own Image
type, you can always declare a(n) (implicit) conversion function/operator, like:
如果由于某些原因,您想要定义自己的Image类型,则可以始终声明一个(n)(隐式)转换函数/运算符,如:
struct Image {
/* explicit */ operator SDL_Surface() const;
// ...
};
and also back to Image
, if you need that:
如果你需要,还可以回到Image:
struct Image {
/* explicit */ Image(SDL_Surface&&);
/* explicit */ Image(SDL_Surface const&);
// ...
};
#2
In C++ you can use the inheritance:
在C ++中,您可以使用继承:
// User view
struct Image; // forward declaration (inclomplete type).
Image* LoadFromFile (...); // You can use pointer to incomplete type
// Implementation view
struct Image: SDL_Surface { }; // here you go !! :-)
Remark: it would be safer to use classes and private inheritance, so that only Image knows that it is an SDL_Surface.
备注:使用类和私有继承会更安全,因此只有Image知道它是SDL_Surface。
In some cases it could be undesirable to to inherit from an existing implementation class (for example if you'd need a virtual destructor and the base class doesn't). Then the PIMPL idiom could be an alternative (at the cost of an additional indirection):
在某些情况下,从现有的实现类继承可能是不可取的(例如,如果您需要虚拟析构函数而基类不需要)。那么PIMPL习惯用法可能是另一种选择(以额外的间接为代价):
//User View unchanged
struct Image;
int TestImage(Image*z);
//implementation view
struct Image {
struct ImageImpl { int x; }; // nested definition or typedef or whatever
ImageImpl *p; // works in every case, at cost of an extra indirection instead of a pointer
};
int TestImage(Image* z)
{
return z->p->x;
}
The main advantage of PIMPL here, is that you could expose more than just an incomplete type, and hence offer to the clients some useful member functions. But if you don't need this, and as you already work with poitners to the object on the client side, you could as well go directly to composition and have an ImageImpl
member instead of a PIMPL pointer.
PIMPL的主要优点在于,您可以暴露的不仅仅是不完整的类型,因此为客户提供了一些有用的成员函数。但是如果你不需要这个,并且因为你已经与poitners一起使用客户端的对象,你也可以直接进入组合并拥有一个ImageImpl成员而不是PIMPL指针。
In C, you can't use inheritance. But composition would certainly do the trick:
在C中,您不能使用继承。但组成肯定会有所作为:
struct Image {
SDL_Surface s;
};
#3
Such operations are normally done with PIMPL (pointer to implementation) pattern. But if you want to avoid indirections for now, or if the type is incomplete (this isn't the case with SDL_Surface
, but it is with many other SDL classes) you can use pointer to void
, since it can point to any data, and then cast it on the implementation side.
这些操作通常使用PIMPL(指向实现的指针)模式完成。但是如果你现在想要避免间接,或者类型是不完整的(SDL_Surface不是这种情况,但是它与许多其他SDL类一样)你可以使用指向void的指针,因为它可以指向任何数据,然后将其投射到实现方面。
Here, we use std::unique_ptr
to make use of Rule of Zero. Such Image
is now non-copyable, but movable. If you want to be able to copy it, use a value_ptr
-like pointer (not in the standard, but you can easily write such pointer yourself or use a third-party one)
在这里,我们使用std :: unique_ptr来使用Zero of Zero。这样的图像现在是不可复制的,但可移动。如果你想复制它,请使用类似value_ptr的指针(不在标准中,但你可以自己轻松编写这样的指针或使用第三方指针)
#include <memory>
struct ImageDeleter
{
void operator()(void* ptr) const;
};
class Image
{
public: // but don't touch it
std::unique_ptr<void, ImageDeleter> internal;
private:
/* private operations on surface */
public:
/* public operations */
void save(const std::string& path) const;
Image(int width, int height);
};
// EXAMPLE USAGE
// Image img(640, 480);
// img.save("aaa.bmp");
// IN THE DEFINITION FILE
#include <SDL2/SDL.h>
namespace detail
{
SDL_Surface* as_surface(const Image& img)
{
return static_cast<SDL_Surface*>(img.internal.get());
}
}
void ImageDeleter::operator()(void* ptr) const
{
SDL_FreeSurface(static_cast<SDL_Surface*>(ptr));
}
Image::Image(int width, int height) :
internal(SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0))
{
}
void Image::save(const std::string& path) const
{
SDL_SaveBMP(detail::as_surface(*this), path.c_str());
}
#4
If your client code doesn't do anything with the image, other than pass a pointer to it, you can use the Windows API trick:
如果您的客户端代码对图像没有任何作用,除了传递指针之外,您可以使用Windows API技巧:
typedef void *HIMAGE; // Image Handle
HIMAGE LoadImage(...);
#5
In C you can resort to an incomplete type.
在C中,您可以采用不完整的类型。
So you define your API in a header file:
因此,您在头文件中定义API:
myapi.h
struct Image;
struct Image* load_image_from_file(...);
...
Note that your struct Image
type, though available for your clients, is completely hidden from them.
请注意,您的结构图像类型虽然可供您的客户使用,但却完全对它们隐藏。
Now your implementation does the full declaration of your struct:
现在您的实现完成了struct的完整声明:
myapi.c:
struct Image {
/* whatever you want to put here */
/* even SDL_Surface */
/* or pointer to it */
};
/* function bodies */
You bundle the compiled C code (object, static or dynamic library) and the header to your clients.
您将已编译的C代码(对象,静态或动态库)和标头捆绑到您的客户端。
#6
In the header you export you can forward SDL_Surface
and then declare Image
to be a pointer to it. Like:
在导出的标题中,您可以转发SDL_Surface,然后将Image声明为指向它的指针。喜欢:
struct SDL_Surface;
typedef SDL_Surface* Image;
extern Image load_image_from_file(char*);
This way your library can be used without the SDL headers.
However, SDL.dll is still needed.
这样,您的库可以在没有SDL标头的情况下使用。但是,仍然需要SDL.dll。