【问题标题】:Cannot forward declare a C typedef struct - looking for alternative suggestions无法转发声明 C typedef 结构 - 寻找替代建议
【发布时间】:2019-11-01 18:14:08
【问题描述】:

我正在使用第三方 C 库 (Raylib),其中包含宏和 struct 在标头中定义如下:

// raylib.h
#define RED { 230, 41, 55, 255 }     // Red

// Color type, RGBA (32bit)
typedef struct Color {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  unsigned char a;
} Color;

我想将它封装在我自己的 C++ 头文件和名为 engine.hengine.cc 的源文件中,它们只公开相关的函数和宏。

为了确保只有engine.cc 可以访问标头raylib.h,我希望:

  1. engine.h 中转发声明struct Color
  2. #include "raylib.h" 仅在 engine.cc

类似这样的东西 (source):

// engine.h
// Forward declare.
struct Color;
typedef struct Color Color;

namespace Engine {
  namespace Colors {
    const extern Color Red;
  }

  void SetBackgroundColor(Color color);
};

// engine.cc
#include "engine.h"
#include "raylib.h"

namespace Engine {
  namespace Colors {
    const Color Red = RED;
  }

  void SetBackgroundColor(Color color) {
    ::SetBackgroundColor(color);
  }
}

不幸的是,这不起作用,因为我无法转发声明 typedef,因为它也在 typedef 中定义了结构。

我的问题是,如果我想对包括 engine.h 在内的所有其他文件隐藏 raylib.h,最好的选择是什么?

我尝试过并且有效的一种方法是拥有自己的颜色enum class,然后由位于engine.cc 中的匿名命名空间中的辅助函数解析。辅助函数使用switch 语句解决它。但这很乏味,因为我需要为我想使用的每种新颜色编写大量代码。此外,Color 只是具有关联宏的结构之一。还有很多,我不想为每一个都创建这些辅助函数。

【问题讨论】:

  • 为什么是typedef Color color
  • 您的代码为我编译:godbolt.org/z/1XGgu1,请显示minimal reproducible example
  • @AlanBirtles - 谢谢。它可以编译,但engine.h 中的Colorengine.cc 中的Color 不同。当您尝试使用SetBackgroundColor(Engine::Colors::Red) 时,它会显示Cannot cast from Color to Color
  • @AlanBirtles - 关于typedef Color Color,抱歉,我本来打算写typedef struct Color Color
  • 关于structs(以及unions 和enums),C 和C++ 之间存在区别:在C 中,struct Color 只能用作struct Color .因此,为方便起见,typedef struct Color { } Color;。在 C++ 中,struct Color 可以用作Colorstruct 的标识符成为类型的名称 - 更方便(恕我直言)。因此,C++ 中不需要像 C 中这样的 typedefs。

标签: c++ c++17


【解决方案1】:

我解决了这个问题,但是在与raylib.h 相同的命名空间中定义了我自己的struct Color,并创建了一个帮助函数,将engine.cc 中的一个转换为另一个。

// engine.h

namespace Engine {
namespace Colors {
struct Color {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  unsigned char a;
};

extern const Color Red;
extern const Color Green;
extern const Color Blue;
extern const Color Black;

}  // namespace Colors

void SetBackgroundColor(const Colors::Color);
}  // namespace Engine




// engine.cc

namespace Engine {
using RayColor = ::Color;
namespace Colors {
const Color Red = RED;
const Color Green = GREEN;
const Color Blue = BLUE;
const Color Black = BLACK;

namespace {
RayColor GetRayColor(const Color color) {
  return RayColor{color.r, color.g, color.b, color.a};
}
}  // namespace
}  // namespace Colors

void SetBackgroundColor(const Colors::Color color) {
  ::ClearBackground(Colors::GetRayColor(color));
}
}  // namespace Engine

这很有效,因为我可以轻松地将 Engine::Colors::Color 转换为 Color。此外,由于所有颜色宏都只是大括号中的数字,因此我可以轻松地使用它们来分配给我自己的结构。

当然,此解决方案特定于 raylib.h 和我的要求。我希望有一个更通用的解决方案来解决这个问题。

我不确定这种方法对性能的影响有多大,但我现在会尝试一下,如果它破坏了任何东西,我会重新访问。

【讨论】:

  • IMO 这使问题过于复杂,只会让人们(包括您自己)以后尝试阅读代码并想知道为什么有人会这样做
  • @M.M - 我同意你的看法。我希望我不必这样做。但就目前而言,这是我能想到的最佳选择。我对替代方案持开放态度,但必须在标题 engine.h 中包含 raylib.h
  • 我希望有一个更通用的解决方案来解决这个问题。 C++20 将引入 modules,这(我希望)会更通用解决方案。
  • @Eljay - 我查看了模块,不确定它们在这种情况下是否有帮助。解决这个问题的唯一方法是,如果我可以在没有完整定义的情况下前向声明一个类型和一个变量(不仅仅是指向前向声明类型的指针或引用)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多