game maker
Gebruikersnaam:
Wachtwoord:
Home Info Forums Help
Welkom, Gast. Alsjeblieft inloggen of registreren.
De activerings e-mail gemist?
+  Forums
|-+  Werken met Game Maker
| |-+  Gevorderden (Moderators: Erik Leppen, ericlegomeer, BlueMoonProductions)
| | |-+  Hulp nodig bij begin DLL in C++
Pagina's: [1]
« vorige volgende »
Print
Advertenties

wwvar
Gebruiker

Offline Offline

Berichten: 31


« Gepost op: 2 Juni 2018, 15:02:32 »

Gegroet,
Ik zou graag een DLL maken die ik vanuit GM kan gebruiken, maar ik heb enkele vragen hierover.
1. Wat is het "template" dat ik gebruik om de functies te exporten naar Game Maker?
2. Hou houd ik het beste data bij in de DLL, bijvoorbeeld in objecten van een klasse? Houd ik ergens een lijst met pointers naar objecten bij, zodat ik aan GM de index van deze lijst kan meegeven?

Ik heb CLion op Windows. Merk op dat ik voor de rest geen vragen over C++ zelf heb, enkel de interactie met Game Maker.

(CodeBlocks is ook ter beschikking)


Edit even ter verduidelijking van #2. Stel nu dat ik de functie makeObject() maak en aan GM doorgeef, en daarin zet ik iets van Obj object = new Obj;
Nu is dit object aangemaakt maar wanneer de functie gedaan is, is de pointer wel kwijt.

« Laatste verandering: 2 Juni 2018, 16:56:40 door wwvar »
Naar boven Gelogd

Flumble
Gebruiker


Offline Offline

Berichten: 2018


« Antwoord #1 Gepost op: 11 Juni 2018, 16:41:21 »

De "template" die je nodig hebt is extern "C" __declspec(dllexport) voor een functie. Dit gaat het makkelijkst met een macro:
Code:
#define function extern "C" __declspec(dllexport)
...
function double add(double a, double b) {
  return a+b;
}
(Let op dat je alleen doubles en char*s heen en weer kan sturen met gamemaker.)

Om dingen te bewaren tussen functieaanroepen, moet je ze globaal opslaan. Voor je lijst van objecten kan je bijvoorbeeld een std::vector<Obj> definiŽren buiten de functies. Of, als je je objecten als een resource id (zoals sprites, buffers, datastructuren) aan gamemaker wil geven, plaats de objecten in een std::map<int,Obj>. Hou vervolgens een globale teller nextObjID bij die je elke keer ophoogt als je een nieuwe Obj maakt en in de map stopt:
Code:
//beetje uit de losse pols, beetje uit een DLL-project van 8 jaar geleden

#include <map>

#define function extern "C" __declspec(dllexport)
using namespace std;

int nextObjID = 1;
map<int, Obj> objs;

function double maakObj(double iets)
{
int nieuwID = nextObjID++;
Obj nieuwObj(iets);
objs[nieuwID] = nieuwObj;
//of, als je geen argumenten nodig hebt in de constructor,
//vervang de bovenstaande 2 regels door `Obj& nieuwObj = objs[nieuwID];`

return nieuwID;
}

function double verwijderObj(double _ind)
{
int ind = (int)_ind; //corrigeer het type

if (objs.erase(ind))
return true;
//else ind bestond niet
}

function double doeIets(double _ind, double _iets)
{
int ind = (int)_ind; //corrigeer het type
bool iets = (bool)_iets; //corrigeer het type

if (objs.find(ind) != objs.end()) {
Obj& object = objs[ind];
return object.doeWatWelOfNiet(iets);
}
//else ind bestaat niet
}


Naar boven Gelogd

wwvar
Gebruiker

Offline Offline

Berichten: 31


« Antwoord #2 Gepost op: 16 Juni 2018, 12:06:10 »

Dankjewel, dit is precies wat ik zocht. Werkt perfect!

Naar boven Gelogd

wwvar
Gebruiker

Offline Offline

Berichten: 31


« Antwoord #3 Gepost op: 20 Juni 2018, 12:28:01 »

Verder heb ik nog enkele problemen met (lange) strings.
Aangezien in Game Maker een string reversen erg lang kan duren (en niet lineair groeit, bv bij mij 20000 tekens: 0.25 sec, 120000 tekens: 6 sec, 160000 tekens: 20 sec), heb ik gewoon een string_reverse in C++ gemaakt, daar aan de implementatie zal wel niets mis zijn (het is erg simpel). Het werkt en is veel sneller natuurlijk, maar bij lange strings (vanaf ~140000 tekens) krijg ik een error


Heb in de C++ code enkele asserts toegevoegd, die geven geen problemen
GML:
#include "main.h"
#include <string>
#include "assert.h"

using namespace std;

export const char* string_reverse(char* input_c, double lengthd)
{
    unsigned int length = (unsigned int)lengthd;

    string input = string(input_c);

    assert(input.size() == length); //, "input.size() == length");

    string output;

    for(int i = input.size()-1; i >= 0; i--)
    {
        output += input[i];
    }

    assert(output.size() == length);

    return output.c_str();
}

Naar boven Gelogd

Flumble
Gebruiker


Offline Offline

Berichten: 2018


« Antwoord #4 Gepost op: 20 Juni 2018, 18:00:03 »

Als het goed is, is de pointer output.c_str() ongeldig zodra de functie klaar is. output wordt namelijk automatisch opgeruimd en .c_str() is slechts een pointer naar die string, geen kopie van je string.
Dat je er geen problemen mee hebt tot je hele lange strings gebruikt, komt denk ik omdat de string toevallig niet overschreven wordt op het moment dat je hem in GM gebruikt.

Ik hoop dat dit wel werkt:
Code:
#include <string>
#include <map>
#include <variant>

#define gmexport extern "C" __declspec(dllexport)

namespace gm {
  /**
   * Type aliases for dll functions
   */
  using string = const char*;
  using real = double;
  using variable = std::variant<string,real>;

  using VoidIntIntFunPtr = void (*)(int,int);
  using IntIntVarFunPtr = int(*)(int,...);
  using BoolIntPCharDoubleFunPtr = bool(*)(int,string,real);
  using BoolIntPCharPCharFunPtr = bool(*)(int,string,string);

  VoidIntIntFunPtr createAsynEventWithDSMap;
  IntIntVarFunPtr createDsMap;
  BoolIntPCharDoubleFunPtr dsMapAddDouble;
  BoolIntPCharPCharFunPtr dsMapAddString;
  const int EVENT_OTHER_SOCIAL = 70;
  std::string* lastReturnString;

  /**
   * gm::stringResult : a -> gm::string
   *
   * Stores a string-like object as a C string for a return value and returns a pointer to it.
   */
  template<typename T> inline string stringResult(T str) {
    if (lastReturnString)
      delete lastReturnString;

    lastReturnString = new std::string(str);
    return lastReturnString->c_str();
  }

  /**
   * : a, b=int -> gm::resource a b
   *
   * Thin wrapper for a map. Allows easy management of objects as resources.
   * Provides `add : a -> b` to store a new object and returns its associated id.
   */
  template<typename T, typename K = int> class resource: public map<K, T> {
  private:
    K id;
  public:
    const K& add(const T& value) {
      insert(++id, value);
      return id;
    }
  }

  /**
   * gm::toGMMap : std::map std::string gm::variable -> int
   *
   * Copies a c++ map with string keys and string/real values to one that can be handled in gamemaker.
   */
  int toGMMap(const std::map<std::string,variable> inmap) {
    int outmap = createDsMap(0);

    for (const auto& item : inmap) {
      if (std::holds_alternative<string>(item.second))
        dsMapAddString(outmap, item.first.c_str(), std::get<string>(item.second).c_str());
      else
        dsMapAddDouble(outmap, item.first.c_str(), std::get<real>(item.second));
    }

    return outmap;
  }
}

//https://help.yoyogames.com/hc/en-us/articles/216755258-Returning-Values-From-An-Extension-Asynchronously-GMS-v1-3-
gmexport void RegisterCallbacks(char *createAsynEventWithDSMap, char *createDsMap, char *dsMapAddDouble, char *dsMapAddString) {
  gm::createAsynEventWithDSMap = (VoidIntIntFunPtr)createAsynEventWithDSMap;
  gm::createDsMap = (IntIntVarFunPtr)createDsMap;
  gm::dsMapAddDouble = (BoolIntPCharDoubleFunPtr)dsMapAddDouble;
  gm::dsMapAddString = (BoolIntPCharPCharFunPtr)dsMapAddString;
}
(ja, hij bevat een en ander extra, maar misschien wil je iemand daar later nog gebruik van maken) (het kan zijn dat het niet compileert, want ik heb het vrijwel blind geschreven en het bevat C++17-functies)

O en gebruik alsjeblieft methodes van c++ zelf (per karakter een nieuwe string bouwen is namelijk zonde):
Code:
#include <string>
#include <algorithm>
#include "gm.h"

using namespace std;

gmexport gm::string string_reverse(gm::string str) {
  string result(str);
  reverse(result.begin(), result.end());
  return gm::stringResult(result);
}

« Laatste verandering: 20 Juni 2018, 21:32:12 door Flumble »
Naar boven Gelogd

wwvar
Gebruiker

Offline Offline

Berichten: 31


« Antwoord #5 Gepost op: 20 Juni 2018, 18:13:33 »

Dankjewel voor het heel uitgebreide antwoord, een beetje een domme fout van mij, maar ben het niet gewoon om met char* te werken ipv strings.
Verder vindt hij bij mij <variant> niet, en wilt hij ook geen const char* omzetten naar char* (daarom dat mijn oorspronkelijke functie const char* teruggaf). Dit heb ik opgelost door even je using string, using real weg te halen en dan "inline string stringResult(..." te vervangen door "inline char* stringResult(...", en heb je een klein foutje lastReturnString.c_str();  die ->c_str() moet zijn.
Zo compileert het wel en werkt het voor zover ik zie wel.

En mijn excuses voor het niet gebruiken van reverse, ik ken C++ nog niet zo goed

« Laatste verandering: 20 Juni 2018, 18:21:11 door wwvar »
Naar boven Gelogd

Flumble
Gebruiker


Offline Offline

Berichten: 2018


« Antwoord #6 Gepost op: 21 Juni 2018, 01:10:32 »

en wilt hij ook geen const char* omzetten naar char* (daarom dat mijn oorspronkelijke functie const char* teruggaf).
Ah, ik had eroverheen gelezen dat string::c_str een const teruggeeft. Werkt het goed als using string = const char*? Ik heb dat en de -> aangepast in m'n vorige bericht.

Mogelijk dat <variant> en andere C++17-features beschikbaar worden als je CLion updatet. (of MinGW of welke compiler je ook maar gebruikt vanuit CLion)

Naar boven Gelogd

wwvar
Gebruiker

Offline Offline

Berichten: 31


« Antwoord #7 Gepost op: 21 Juni 2018, 09:27:43 »

Ik ben codeblocks aan het gebruiken nu, en ik heb die using string, real weggehaald omdat ik het dan te verwarrend vond met string/std::string. Maar het zou dan moeten werken aangezien ik nu in de functie string_reverse en stringResult het type naar const char* heb veranderd.

De rest van jouw code die ik niet gebruik heb ik even weggecomment (en weggehaald om het volgende wat korter te houden)
GML:
#ifndef GM_H_INCLUDED
#define GM_H_INCLUDED

#include <string>
#include <map>

#define gmexport extern "C" __declspec(dllexport)

namespace gm {

    std::string* lastReturnString;

  /**
   * gm::stringResult : a -> gm::string
   *
   * Stores a string-like object as a C string for a return value and returns a pointer to it.
   */


  template<typename T> inline const char* stringResult(T str) {
    if (lastReturnString)
      delete lastReturnString;

    lastReturnString = new std::string(str);
    return lastReturnString->c_str();
  }
}
#endif // GM_H_INCLUDED

GML:
#include <string>
#include "assert.h"
#include <algorithm>
#include "gm.h"

using namespace std;

gmexport const char* string_reverse(char* str, double lengthd)
{
    unsigned int length = (unsigned int)lengthd;

    string result(str);
    assert(result.size() == length); // zou nu weg mogen, was uiteindelijk om te zien of de string wel correct werd meegegeven

    reverse(result.begin(), result.end());
    return gm::stringResult(result);
}

« Laatste verandering: 21 Juni 2018, 10:48:43 door wwvar »
Naar boven Gelogd

Advertenties
« vorige volgende »
Pagina's: [1]
Print


Topic Informatie
0 geregistreerde leden en 1 gast bekijken dit topic.

Ga naar:  

Powered by SMF 1.1.21 | SMF © 2006-2007, Simple Machines
www.game-maker.nl © 2003-2020 Nederlandse Game Maker Community