Как сделать функцию получения для двойного указателя? - PullRequest
1 голос
/ 16 ноября 2010

Мне нужно изменить проект с открытым исходным кодом, чтобы предотвратить повторное использование кода (более эффективно просто создать функцию GetGameRulesPtr (), чем продолжать загружать его в движок. Проблема в том, что он сохраняется как void ** g_pGameRules Я никогда не понимал концепцию указателя на указатель, и я немного растерялся.

Я создаю функцию GetGameRules () для извлечения этого указателя, но я не уверен, должна ли моя функция-получатель иметь тип void * ret и затем возвращать * g_pGameRules, или как именно я должен поступить об этом. Сейчас я на самом деле изучаю использование указателя, но хотел найти подходящий метод для изучения.

Вот код, строки 58-89 - это функция SDK, которая извлекает указатель g_pGameRules из игрового движка. К другим функциям я добавляю функцию получения.

// extension.cpp

class SDKTools_API : public ISDKTools
{
public:
    virtual const char *GetInterfaceName()
    {
        return SMINTERFACE_SDKTOOLS_NAME;
    }

    virtual unsigned int GetInterfaceVersion()
    {
        return SMINTERFACE_SDKTOOLS_VERSION;
    }

    virtual IServer *GetIServer()
    {
        return iserver;
    }

    virtual void *GetGameRules()
    {
        return *g_pGameRules;
    }
} g_SDKTools_API;

// extension.h

namespace SourceMod
{
    /**
     * @brief SDKTools API.
     */
    class ISDKTools : public SMInterface
    {
    public:
        virtual const char *GetInterfaceName() = 0;
        virtual unsigned int GetInterfaceVersion() = 0;
    public:
        /**
         * @brief Returns a pointer to IServer if one was found.
         *
         * @return          IServer pointer, or NULL if SDKTools was unable to find one.
         */
        virtual IServer* GetIServer() = 0;

        /**
         * @brief Returns a pointer to GameRules if one was found.
         *
         * @return          GameRules pointer, or NULL if SDKTools was unable to find one.
         */
        virtual void* GetGameRules() = 0;
    };
}

// vglobals.cpp

void **g_pGameRules = NULL;
void *g_EntList = NULL;


void InitializeValveGlobals()
{
    g_EntList = gamehelpers->GetGlobalEntityList();

    char *addr;

#ifdef PLATFORM_WINDOWS
    /* g_pGameRules */
    if (!g_pGameConf->GetMemSig("CreateGameRulesObject", (void **)&addr) || !addr)
    {
        return;
    }

    int offset;
    if (!g_pGameConf->GetOffset("g_pGameRules", &offset) || !offset)
    {
        return;
    }
    g_pGameRules = *reinterpret_cast<void ***>(addr + offset);
#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
    /* g_pGameRules */
    if (!g_pGameConf->GetMemSig("g_pGameRules", (void **)&addr) || !addr)
    {
        return;
    }
    g_pGameRules = reinterpret_cast<void **>(addr);
#endif
}

Ответы [ 3 ]

1 голос
/ 16 ноября 2010

Вы хотите вернуть void* и выполнить приведение к соответствующему SomeType** в коде реализации. Это потому, что void** имеет странную семантику (которую я не могу найти в Google сейчас). Он также сообщает пользователю больше информации, чем ему действительно нужно. Весь смысл использования void* для начала заключался в том, чтобы не предоставлять пользователю информацию, которая ему не нужна.

Если это вариант, я бы лично рекомендовал вообще избегать void* и просто предоставлять непрозрачный тип ссылки, чтобы они могли вызывать ваши API. Один из способов сделать это - определить поддельную структуру, такую ​​как struct GameObjectRef {};, и передать пользователю обратно GameObjectRef*, приведенный от любого указателя, который фактически использует ваша система. Это позволяет пользователю писать строго типизированный код, поэтому он не может случайно предоставить неверный тип указателя для ваших функций, как они могут это сделать с помощью void*.

Как работают указатели (и указатели на указатели):

Представьте, что вы спрашиваете меня, где живет ваша тетя. Затем я вручаю вам лист бумаги с адресом, по которому можно перейти. Этот лист бумаги - указатель на дом.

Теперь возьмите этот листок бумаги с адресом, сфотографируйте его цифровой камерой и поместите изображение на свой личный вики-сайт.

Теперь, если ваша сестра звонит, спрашивая адрес вашей тети, просто скажите ей, чтобы он нашел ее в вашей вики. Если она попросит URL, напишите ее на листе бумаги для нее. Этот второй лист бумаги - указатель на указатель на дом.

Вы можете видеть, что адрес не совпадает с реальным. Тот факт, что у кого-то есть адрес вашего сайта, не означает, что он знает адрес вашей тети. И только потому, что у них есть адрес твоей тети, не значит, что они стучат в ее дверь. То же самое верно для указателей на объекты.

Вы также можете увидеть, как вы можете делать копии адресов (указателей), но это не делает копию базового объекта. Когда вы фотографируете адрес своей тети, ваша тетя не получает новый блестящий дом.

И вы можете видеть, как разыменование указателя приведет вас обратно к исходному объекту. Если вы зайдете на вики-сайт, вы получите адрес своей тети. Если вы едете по этому адресу, вы можете оставить посылку у ее порога.

Обратите внимание, что это не идеальные метафоры, но они достаточно близки, чтобы быть несколько описательными. Реальные указатели на указатели намного чище, чем эти примеры. Они описывают только две вещи - тип конечного объекта (скажем, GameObject) и количество уровней косвенности (скажем, GameObject** - два уровня).

0 голосов
/ 16 ноября 2010

Указатель на указатель полезен, если вы хотите изменить указатель (например, на больший блок памяти, если у вас закончилась память), но сохраните общую ссылку на элемент, куда бы он ни перемещался.

Если вы не собираетесь перераспределять или перемещать указанный блок, то разыменование, как у вас в геттере, подойдет.Я не смотрел на библиотеку, которую вы используете, но следует учитывать, что она может иметь подсчет ссылок при получении экземпляра, чтобы гарантировать, что объект не будет изменен после получения указателя.

Итак, я бы порекомендовал вам посмотреть, есть ли в библиотеке какие-либо «фабричные» функции или «экземплярные» функции создания и использовать их.

-Dan8080

0 голосов
/ 16 ноября 2010

Я думаю, что это не двойной указатель, а указатель на указатель, и да, если вы хотите получить void *, вы должны вернуть * g_pGameRules.

Суть в том, что указатели похожи на уровни.Вы должны показать, какой уровень вы хотите получить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...