对我来说,矢量布局看起来像这样:
struct MyViewOnHowVectorLooksLike
{
public:
int* _first, * _last, * _end;
};
已知参数:
std::vector<Type>
对象的地址<Type>
问题:
有没有办法通过基内存地址访问
std::vector
中的第一个和最后一个元素,即上面测试用例中的&Test
,而不是通过调用std::vector
函数的任何内置方法,例如 [0]
、.front()
等; (我这样做的原因是有一个静态反射系统,它可以输出任何给定的书面向量类型的内容)
struct FTestData {
int data;
};
class TestClass {
public:
// test array
std::vector<FTestData> testArray;
TestClass(){}
};
// TEST CASE
FTestData testCon1 = FTestData();
testCon1.data = 111;
FTestData testCon2 = FTestData();
testCon2.data = 222;
TestClass Test = TestClass();
Test.testArray.push_back(testCon1);
Test.testArray.push_back(testCon2);
std::vector<FTestData>* TestData = (std::vector<FTestData>*)((size_t)&Test);
size_t ptr = (size_t)TestData;
FTestData& TestElement1 = (*TestData)[0];
size_t realFirstElementAddress = (size_t)&TestElement1;
// try to get the address of the first element in the vector but it gives me the wrong address
size_t CustomFirstElementAddress = (*(size_t*)(ptr));
// expecting those two to be the same but it wasnt
assert(realFirstElementAddress == CustomFirstElementAddress);
我以为
_First
和_Last
就在基址旁边,但它一直输出错误的结果。
附加组件: 我想要解决的真正问题是拥有一个带有我自己的反射系统的细节面板,这可以让我做这样的事情。它适用于大多数常见类型和嵌套类。
{
if (field.Type == "int") {
int location[1];
auto& value = *(int*)((size_t)&Component + Offset + field.Offset);
memcpy(location, &value, sizeof(location));
if (ImGui::DragInt(fieldName.c_str(), location, 0.1f, 0.0f, 0.0f, "%.1f"))
{
memcpy(&value, location, sizeof(location));
}
}
else if (field.Type == "std::string") {
char buffer[256];
memset(buffer, 0, sizeof(buffer));
auto& value = *(std::string*)((size_t)&Component+ Offset + field.Offset);
strcpy(buffer, value.c_str());
if (ImGui::InputText(fieldName.c_str(), buffer, sizeof(buffer))) {
value = std::string(buffer);
}
}
else if (field.Type == "bool") {
bool location[1];
auto& value = *(bool*)((size_t)&Component + Offset + field.Offset);
memcpy(location, &value, sizeof(location));
if (ImGui::Checkbox(fieldName.c_str(), location)) {
memcpy(&value, location, sizeof(location));
}
}
else if (field.Type == "float") {
float location[1];
auto& value = *(float*)((size_t)&Component + Offset + field.Offset);
memcpy(location, &value, sizeof(location));
if (ImGui::DragFloat(fieldName.c_str(), location, 0.1f, 0.0f, 0.0f, "%.1f"))
{
memcpy(&value, location, sizeof(location));
}
}
}
对我来说,矢量布局看起来像这样:
您正在对实施细节的事情做出假设。您的编译器对
std::vector
的特定实现可能会以这种方式工作(您自己查看 <vector>
的内部),但是 std::vector
的不同实现可以按照自己的意愿布局和管理其内部成员,只要公共接口的行为方式相同C++ 标准定义了它。您甚至根本无法保证数据成员是指针。实现 vector
的方法不止一种。
有没有办法通过基内存地址访问
中的第一个和最后一个元素std::vector
不,尝试这样做将是未定义的行为。
我以为
和_First
就在基址旁边,但它一直输出错误的结果。_Last
编译器可以随意对类数据成员进行排序,在数据成员之间插入填充等。因此您根本无法做出您想要做出的假设。
我想要解决的真正问题是拥有一个带有我自己的反射系统的细节面板,这可以让我做这样的事情。它适用于大多数常见类型和嵌套类。
有可用于 C++ 的第 3 方反射库,您应该考虑使用其中之一,而不是从头开始编写自己的反射库。
话虽这么说,您显示的代码可以稍微简化一下,例如:
{
auto* ptr = reinterpret_cast<char*>(&Component) + Offset + field.Offset;
if (field.Type == "int") {
auto& value = *reinterpret_cast<int*>(ptr);
int temp = value;
if (ImGui::DragInt(fieldName.c_str(), &temp, 0.1f, 0.0f, 0.0f, "%.1f")) {
value = temp;
}
}
else if (field.Type == "std::string") {
auto& value = *reinterpret_cast<std::string*>(ptr);
/* see https://stackoverflow.com/questions/69046648/
for how to get rid of this char[] buffer and use
ImGui::InputText() with std::string directly...
std::string temp = value;
if (ImGui::InputText(fieldName.c_str(), &temp) {
value = temp;
}
*/
char buffer[256] = {};
strncpy(buffer, value.c_str(), std::size(buffer));
if (ImGui::InputText(fieldName.c_str(), buffer, std::size(buffer))) {
value = buffer;
}
}
else if (field.Type == "bool") {
auto& value = *reinterpret_cast<bool*>(ptr);
bool temp = value;
if (ImGui::Checkbox(fieldName.c_str(), &temp)) {
value = temp;
}
}
else if (field.Type == "float") {
auto& value = *reinterpret_cast<float*>(ptr);
float temp = value;
if (ImGui::DragFloat(fieldName.c_str(), &temp, 0.1f, 0.0f, 0.0f, "%.1f")) {
value = temp;
}
}
}
然后进一步清理一下:
template<typename T>
void getUiInput(const std::string&, T& t) { }
template<>
void getUiInput<int>(const std::string& fieldName, int& value) {
int temp = value;
if (ImGui::DragInt(fieldName.c_str(), &temp, 0.1f, 0.0f, 0.0f, "%.1f")) {
value = temp;
}
}
template<>
void getUiInput<bool>(const std::string& fieldName, bool& value) {
bool temp = value;
if (ImGui::Checkbox(fieldName.c_str(), &temp)) {
value = temp;
}
}
template<>
void getUiInput<float>(const std::string& fieldName, float& value) {
float temp = *value;
if (ImGui::DragFloat(fieldName.c_str(), &temp, 0.1f, 0.0f, 0.0f, "%.1f")) {
value = temp;
}
}
template<>
void getUiInput<std::string>(const std::string& fieldName, std::string& value) {
/* see https://stackoverflow.com/questions/69046648/
for how to get rid of this char[] buffer and use
ImGui::InputText() with std::string directly...
std::string temp = value;
if (ImGui::InputText(fieldName.c_str(), &temp )) {
value = temp;
}
*/
char buffer[256] = {};
strncpy(buffer, value.c_str(), std::size(buffer));
if (ImGui::InputText(fieldName.c_str(), buffer, IM_ARRAYSIZE(buf))) {
value = buffer;
}
}
{
auto* ptr = reinterpret_cast<char*>(&Component) + Offset + field.Offset;
if (field.Type == "int") {
getUiInput(fieldName, *reinterpret_cast<int*>(ptr));
}
else if (field.Type == "std::string") {
getUiInput(fieldName, *reinterpret_cast<std::string*>(ptr));
}
else if (field.Type == "bool") {
getUiInput(fieldName, *reinterpret_cast<bool*>(ptr));
}
else if (field.Type == "float") {
getUiInput(fieldName, *reinterpret_cast<float*>(ptr));
}
}