Программист выходного дня, новичок в C++, сражается с войной указателей!
Я пытаюсь создать код на C++ для получения данных из API с использованием вектора, который передается через аргумент void * в функцию обратного вызова.
Возвращенные данные будут переданы обратно в lua, который является интерфейсом, используемым в приложении. Часть lua - это не то, что меня сбивает с толку, это проблема с заполнением моей структуры данными из обратного вызова.
У меня все это работало с использованием простых указателей int, но мне сказали о предпочтительном способе сделать это с помощью структуры, и мне было предложено сделать следующее:
1 - define a struct that contains the 6 ints
2 - create an std::vector that can contain instances of the new struct
3 - pass a pointer to the vector as refcon into XPLMGetAllMonitorBoundsGlobal
inside the callback function:
4 - cast the refcon pointer back to the vector type
5 - create a new instance of the struct and fill it with the 6 ints that are passed to the callback function
6 - push that instance into the vector
7 - when XPLMGetAllMonitorBoundsGlobal returns, the vector will be filled with the bounds of all screens
8 - convert the vector into a lua-compatible thing, probably a two-dimensional array
push that array into lua
Вот код, который я придумал, сделал много поисков Google / Stackoverflow, чтобы приблизиться к этому, но даже несмотря на то, что код компилируется, он вешает приложение или вызывает сбои сегментов, зависит от того, что я настроил. Я думаю, что основная проблема заключается в том, как я возвращаю указатель refcon обратно к векторному типу, слишком много ссылок / разыменований указателей, и, вероятно, есть и другие проблемы.
struct MonitorBoundsStructure // #1
{
int MonitorIndex;
int LeftBx;
int TopBx;
int RightBx;
int BottomBx;
int RefCon;
};
static void LuaReceiveMonitorBoundsOS_t(int inMonitorIndex, int inLeftBx, int inTopBx, int inRightBx, int inBottomBx, void * refcon)
{
vector<MonitorBoundsStructure*>& MonitorBounds = *reinterpret_cast<vector<MonitorBoundsStructure*> *>(refcon); #4
//vector<MonitorBoundsStructure> MonitorBounds = reinterpret_cast<vector<MonitorBoundsStructure*> *>(refcon);
//vector<MonitorBoundsStructure> MonitorBounds = *reinterpret_cast<vector<MonitorBoundsStructure> *>(refcon);
//vector<MonitorBoundsStructure> MonitorBounds = reinterpret_cast<vector<MonitorBoundsStructure> *>(refcon);
//vector<MonitorBoundsStructure>* MonitorBounds = static_cast<vector<MonitorBoundsStructure> *>(refcon);
MonitorBoundsStructure returnData; //{0,0,0,0,0,0}; #5
returnData.MonitorIndex = inMonitorIndex;
returnData.LeftBx = inLeftBx;
returnData.TopBx = inTopBx;
returnData.RightBx = inRightBx;
returnData.BottomBx = inBottomBx;
returnData.RefCon = *(int *)refcon;
MonitorBounds.push_back(&returnData); // #6
}
static int LuaXPLMGetAllMonitorBoundsOSTest()
{
//std::vector<std::shared_ptr<MonitorBoundsStructure>> MonitorBounds;
vector<MonitorBoundsStructure> MonitorBounds; // #2
XPLMGetAllMonitorBoundsOS(LuaReceiveMonitorBoundsOS_t, &MonitorBounds); // #3
int i = 0;
for (vector<MonitorBoundsStructure>::iterator it = MonitorBounds.begin(); it != MonitorBounds.end(); ++it)
{
i++;
logMsg(logToAll, string("MonitorBounds ").append(to_string(i)));
}
return 1;
}
Если я закомментирую MonitorBounds.push_back (& returnData), код по крайней мере дойдет до того места, где структура внутри обратного вызова заполнена правильными данными, я ударяюсь о стену, возвращая эту структуру вызывающей функции, то есть мой приведенный void * to vector неверно по крайней мере.
Я оставил в комментариях некоторые из своих других попыток, чтобы показать, что я пробовал.
Я близок к решению или далеко?
Решение: Спасибо Теду Люнгмо за предоставленное решение, даже за то, что он написал тестовый код на своей собственной системе. Моя проблема, как я и подозревал, заключалась в приведении указателя на вектор. Я включил ответы как push_back, так и emplace_back (я нахожу их гораздо аккуратнее).
-функция обратного вызова:
static void LuaReceiveMonitorBoundsOS_e(int inMonitorIndex, int inLeftBx, int inTopBx, int inRightBx, int inBottomBx, void * refcon)
{
auto& MonitorBounds = *reinterpret_cast<vector<MonitorBoundsStructure>*>(refcon);
MonitorBoundsStructure returnData;
returnData.MonitorIndex = inMonitorIndex;
returnData.LeftBx = inLeftBx;
returnData.TopBx = inTopBx;
returnData.RightBx = inRightBx;
returnData.BottomBx = inBottomBx;
MonitorBounds.push_back(returnData);
}
а также
static void LuaReceiveMonitorBoundsOS_e(int inMonitorIndex, int inLeftBx, int inTopBx, int inRightBx, int inBottomBx, void * refcon)
{
auto& MonitorBounds = *reinterpret_cast<vector<MonitorBoundsStructure>*>(refcon);
MonitorBounds.emplace_back(
MonitorBoundsStructure{
inMonitorIndex,
inLeftBx,
inTopBx,
inRightBx,
inBottomBx
}
);
}
-вызывающая функция:
static int LuaXPLMGetAllMonitorBoundsOSTestE(lua_State *L)
{
vector<MonitorBoundsStructure> MonitorBounds;
XPLMGetAllMonitorBoundsOS(LuaReceiveMonitorBoundsOS_e, &MonitorBounds);
for (auto& m : MonitorBounds) {
std::cout << m.MonitorIndex << "\n";
}
return 1;
}





MonitorBoundsStructure returnData; //{0,0,0,0,0,0}; #5
MonitorBounds.push_back(&returnData); // #6
После этого returnData выходит из области видимости и уничтожается. Указатель, который у вас теперь есть в MonitorBounds, недействителен.
Также похоже, что вы выполняете приведение к неправильному типу. Если это вектор:
vector<MonitorBoundsStructure> MonitorBounds;
Вы должны указать это в своем обратном вызове:
auto& MonitorBounds = *reinterpret_cast<vector<MonitorBoundsStructure>*>(refcon);
И нажмите всю структуру, а не указатель на нее:
MonitorBounds.push_back(returnData);
Обновлено: я сделал тестовую версию со своим собственным XPLMGetAllMonitorBoundsOS, чтобы убедиться, что он работает и выглядит нормально.
#include <iostream>
#include <string>
#include <vector>
typedef void (*XPLMReceiveMonitorBoundsOS_f)( int inMonitorIndex,
int inLeftPx,
int inTopPx,
int inRightPx,
int inBottomPx,
void* inRefcon);
struct MonitorBoundsStructure // #1
{
int MonitorIndex;
int LeftBx;
int TopBx;
int RightBx;
int BottomBx;
};
static void LuaReceiveMonitorBoundsOS_t(int inMonitorIndex, int inLeftBx, int inTopBx,
int inRightBx, int inBottomBx, void* refcon)
{
auto& MonitorBounds = *reinterpret_cast<std::vector<MonitorBoundsStructure>*>(refcon);
MonitorBounds.emplace_back(
MonitorBoundsStructure{
inMonitorIndex,
inLeftBx,
inTopBx,
inRightBx,
inBottomBx
}
);
}
void XPLMGetAllMonitorBoundsOS(XPLMReceiveMonitorBoundsOS_f callback, void* inRefcon)
{
callback(0,100,0,100,100, inRefcon);
callback(1,99,1,99,99, inRefcon);
callback(2,100,1,100,100, inRefcon);
}
static int LuaXPLMGetAllMonitorBoundsOSTest() {
std::vector<MonitorBoundsStructure> MonitorBounds; // #2
XPLMGetAllMonitorBoundsOS(LuaReceiveMonitorBoundsOS_t, &MonitorBounds); // #3
for (auto& m : MonitorBounds) {
std::cout << m.MonitorIndex << "\n";
}
return 1;
}
int main() {
LuaXPLMGetAllMonitorBoundsOSTest();
}
Выход
0
1
2
У вас есть отладчик, чтобы вы могли точно видеть, где он обнаруживает ошибки? Я бы удалил из структуры член RefCon, а также returnData.RefCon = *(int *)refcon. Если int больше адреса, это вызывает неопределенное поведение.
Добавлена небольшая тестовая версия
Я заставил его работать, как ваше первое предложение, так и последующее использование .emplace_back вместо push_back (что я очень предпочитаю). Проблема, с которой я столкнулся с вашим первым предложением, была связана с некоторым остаточным кодом, который я сохранил в функции обратного вызова, которая работала с простым указателем int, по-видимому, это мешает новому коду. Я плохо держал это там, я думал, что это безвредно, когда это не так. Большое спасибо за вашу помощь!
И забыл упомянуть, что это работает даже с участником RefCon, хотя, думаю, в конце концов удалю, так как оно мне не нужно.
Большой! Что касается push_back и emplace_back: в таких случаях, когда вы создаете что-то с единственной целью добавить это к вектору, emplace_back более эффективен, поскольку он обычно создает структуру на месте для вас. Когда вы сначала создаете структуру, а затем используете push_back, необходимо сделать копию структуры.
Спасибо за быстрый ответ. Я внес изменения в свой cast и push_back, как вы предложили, но получил ошибки сегментирования. Опять же, кажется, вопрос о push_back ...