Как избавиться от dynamic_cast здесь?

Я делаю простой графический движок для своей игры.

Это часть интерфейса:

class Texture { ... };

class DrawContext 
{ 
 virtual void set_texture(Texture* texture) = 0; 
}

Это часть реализации:

class GLTexture : public Texture
{
public:
 ...
 ****** handle;
};

void GLDrawContext::set_texture(Texture* texture)
{
 // Check if it GLTexture, otherwise do nothing.
 if (GLTexture* gl_texture = dynamic_cast<gltexture*>(texture))
 {
 glBindTexture(GL_TEXTURE_2D, gl_texture->handle);
 }
}
</gltexture*>

Имеет ли смысл использовать dynamic_cast здесь? Есть ли способ избежать этого?

5 ответов

Не могли бы вы попытаться обратить вспять проблему?

class Texture
{
public:
 virtual void set_texture() = 0;
};

class GLTexture : public Texture
{
public:
 virtual void set_texture();
 ****** handle;
};

void GLTexture::set_texture()
{
 glBindTexture(GL_TEXTURE_2D, handle);
}
class DrawContext 
{ 
 virtual void set_texture(Texture* texture) = 0; 
};

class GLDrawContext : public DrawContext
{
 virtual void set_texture(Texture* texture);
};

void GLDrawContext::set_texture(Texture* texture)
{
 texture->set_texture();
}


Конечно, используйте static_cast вместо этого, хотя вы потеряете некоторую обработку ошибок, если вы передадите фиктивный указатель. Мы используем идею assert_cast для динамического построения отладки и статического для выпуска, чтобы обойти RTTI для такого рода вещей.


Я думаю, что стандартным способом избежать dynamic_cast было бы добавление виртуального метода в класс Texture:

virtual int get_texture_handle() const {return -1;}

Затем переопределите метод только в вашем классе GLTexture:

virtual int get_texture_handle() const {return gl_texture->handle;}

Тогда ваш код вызова будет выглядеть так:

int handle = texture->get_texture_handle();
if (handle >= 0) glBindTexture(GL_TEXTURE_2D, handle);


В качестве альтернативы вы можете попробовать использовать дженерики, чтобы уйти от динамического перевода. Generics позволит вам ловить ошибки во время компиляции (вы никогда не сможете передать текстуру DirectX в GL DrawContext). Кроме того, для динамической отправки не потребуется никаких затрат, и компилятор должен иметь возможность делать встраивание.

namespace GL_impl {

struct Texture {
 ****** handle;
};

struct DrawContext {
 void set_texture(Texture* texture)
 {
 glBindTexture(GL_TEXTURE_2D, texture->handle);
 }
};

} // GL_impl


struct use_GL {
 typedef GL_impl::Texture Texture;
 typedef GL_impl::DrawContext DrawContext;
};

template <class use_impl="">
void f()
{
 typedef typename use_impl::Texture Texture;
 typedef typename use_impl::DrawContext DrawContext;

 Texture t;
 DrawContext ctx;
 ctx.set_texture(&t);
}

void call_f_with_gl()
{
 f<use_gl>();
}
</use_gl></class>


Несколько иной подход, который включает в себя модификацию класса Texture.

class Texture 
{
 virtual void bind_texture(){} 
};
class GLTexture : public Texture 
{
 virtual void bind_texture(); 
};
void GLTexture::bind_texture()
{
 glBindTexture(GL_TEXTURE_2D, handle);
}
class DrawContext 
{ 
 virtual void set_texture(Texture* texture) = 0; 
};
class GLDrawContext : public DrawContext
{
 virtual void set_texture(Texture* texture);
};
void GLDrawContext::set_texture(Texture* texture)
{
 if( texture )
 texture->bind_texture();
}

licensed under cc by-sa 3.0 with attribution.