Speicherplatz in Hauptprogramm für Routine aus DLL reservieren



  • Danke für deine Geduld!

    Leider kommt es immer zu einem invalid cast Error:

    invalid cast from type 'std::array<std::array<char, 1000u>, 100u>::pointer {aka std::array<char, 1000u>*}' to type 'StringParam {aka unsigned char** [100]}'
    

    hier am Beispiel von:

    array<array<char, 1000>, 100> foo;
    copy(PArray, reinterpret_cast<StringParam>(foo.data()));
    

    Ich habe versucht es noch mit array<array<unsigned char, 100>, 1000> foo; etc versucht aber Änderungen in der Form haben es auch nicht gebracht.

    Könntest du bitte noch mal drüber schauen?

    Wäre es eigentlich auch möglich den Array durch char Array[100][1000] zu erstellen, und dann über zwei Pointer darauf zu zeigen und diese dann an die DLL-Routine zu übergeben?


  • Mod

    Argh! Ja ich hab's falsch rum gemacht. Deine Funktion will ja 100 Zeiger auf Zeiger auf unsigned char, nicht einen Zeiger auf 100 Zeiger auf unsigned char. Das macht immer weniger Sinn, aber wenn's so vorgegeben ist, dann ist das eben so.

    Jedenfalls muessen wir bei so einer Struktur tatsaechlich mit ein paar haerteren Mitteln ran und uns den Speicher manuell besorgen:

    #include <cstdio>
    
    typedef unsigned char Pchar;
    typedef Pchar *StringParam[100];
    
    void test(double *values, StringParam strings)
    {
      for(unsigned i = 0; i < 100; ++i)
        sprintf((char*) strings[i], "Blah: %f\n", values[i]);
    }
    
    #include <cstddef>
    #include <algorithm>
    // Wrapper around a C-style list of lists (T**)
    // but with static inner dimension. Makes no sense
    // (why not use an array?) but is needed by external
    // library function.
    template<typename T> class List_of_Lists
    {
    private:
      std::size_t outer;
      std::size_t inner;
      T** internal_data;
    public:
      List_of_Lists(std::size_t outer, std::size_t inner):
        outer(outer), inner(inner), internal_data(new T*[outer]())
      {
        try
          {
            for(std::size_t i = 0; i < outer; ++i)
              {
                internal_data[i] = new T[inner];
              }
          }
        catch (...)
          {
            for(std::size_t i = 0; i < outer; ++i)
              delete[] internal_data[i];       
            delete[] internal_data;
            throw;
          }
      }
    
      ~List_of_Lists()
      {
        for(std::size_t i = 0; i < outer; ++i)
          delete[] internal_data[i];       
        delete[] internal_data;
      }
    
      List_of_Lists(const List_of_Lists& other):
        outer(other.outer),
        inner(other.inner),
        internal_data(new T*[outer]())
      {
        try
          {
            for(std::size_t i = 0; i < outer; ++i)
              {
                internal_data[i] = new T[inner];
                std::copy(other.internal_data[i], 
                          other.internal_data[i] + inner, 
                          internal_data[i]);
              }
          }
        catch (...)
          {
            for(std::size_t i = 0; i < outer; ++i)
              delete[] internal_data[i];       
            delete[] internal_data;
            throw;
          }
      }
    
      List_of_Lists& operator=(List_of_Lists other)
      {
        std::swap(other.inner, inner);
        std::swap(other.outer, outer);
        std::swap(other.internal_data, internal_data);
        return *this;
      }
    
      T** data() { return internal_data; }
      const T* operator[](std::size_t index) const { return internal_data[index]; }
      T* operator[](std::size_t index) { return internal_data[index]; }
    };
    
    #include <iostream>
    #include <vector>
    int main()
    {
      std::vector<double> bar(100);
      for(int i = 0; i < 100; ++i) bar[i] = 25.37326 * i + 7.4747;
    
      List_of_Lists<unsigned char> strings(100, 1000);
      test(bar.data(), strings.data());
    
      for(int i = 0; i < 100; ++i) std::cout << strings[i];
    }
    

    Diese Mal ist es auch getestet, dass es tatsaechlich funktioniert 🙂 .

    Move constructor und move assignment spare ich mir mal, das moege man sich bei Bedarf selber implementieren. Wahrscheinlich werden selbst der Kopierkonstruktor und der Zuweisungsoperator hier nie benutzt werden.



  • Etwas modifiziert um Code-Duplication und das phöse re-throw zu vermeiden:

    template<typename T>
    class List_of_Lists_Base
    {
    protected:
    	std::vector<T*> internal_data;
    
    	explicit List_of_Lists_Base(std::size_t outer)
    		: internal_data(outer) {}
    
    	~List_of_Lists_Base()
    	{
    		for (auto p : internal_data)
    			delete[] p;
    	}
    
    	List_of_Lists_Base(List_of_Lists_Base const&) = delete;
    	List_of_Lists_Base& operator = (List_of_Lists_Base const&) = delete;
    };
    
    // Wrapper around a C-style list of lists (T**) 
    // but with static inner dimension. Makes no sense 
    // (why not use an array?) but is needed by external 
    // library function. 
    template<typename T>
    class List_of_Lists : private List_of_Lists_Base<T>
    {
    private:
    	std::size_t inner;
    
    public:
    	List_of_Lists(std::size_t outer, std::size_t inner)
    		: List_of_Lists_Base(outer), inner(inner)
    	{
    		for (auto& p : internal_data)
    			p = new T[inner];
    	}
    
    	List_of_Lists(List_of_Lists const& other)
    		: List_of_Lists(other.internal_data.size(), other.inner)
    	{
    		for (std::size_t i = 0; i < internal_data.size(); ++i)
    			std::copy(other.internal_data[i], other.internal_data[i] + inner, internal_data[i]);
    	}
    
    	List_of_Lists& operator = (List_of_Lists other)
    	{
    		std::swap(other.inner, inner);
    		std::swap(other.internal_data, internal_data);
    		return *this;
    	}
    
    	T const* const* data() const { return internal_data.data(); }
    	T** data() { return internal_data.data(); }
    
    	T const* operator[](std::size_t index) const { return internal_data[index]; }
    	T* operator[](std::size_t index) { return internal_data[index]; }
    };
    

  • Mod

    hustbaer schrieb:

    Etwas modifiziert um Code-Duplication und das phöse re-throw zu vermeiden:

    Ich wusste schon, dass genau diese Antwort kommen würde, während ich das schrieb. Aber in dem Fall siegte die Faulheit 😃 . Ich rede mich aber trotzdem mal damit raus, dass der TE meinen Code wohl eher versteht (wenn er überhaupt noch was versteht), auch wenn es nicht gerade eine Demonstration von best-practices* ist.

    *: Mein Code zeigt ja sogar ganz im Gegenteil, wieso es keine gute Idee ist, mehrere Ressourcen in einem Objekt zu halten.



  • Meine Faulheit äussert sich darin dass ich gerade solchen Code schreibe. Weil ich keine Lust habe mir über Dinge den Kopf zu zerbrechen, wie eben ob alles in jedem Fall richtig freigegeben wird etc. 😉
    Und weil ich noch viel weniger Lust habe das wiederholt zu tun - wie wenn ich ein Programm lese, Fehler suche, Änderungen machen muss etc.
    (Was in dem Fall natürich wurscht wäre, aber das is einfach schon so ne starke Angewohnheit...)

    ~Mir sind Leute suspekt die dreckigen Test- oder Demo-Code schreiben. Ich bin nämlich der Meinung dass die meisten davon auch dreckigen Produktiv-Code schreiben. ;)~



  • Ich danke vielmals - Es geht alles - einfach wunderbar 🙂
    Das hätte ich aber selber garantiert nicht hinbekommen 🙄



  • Was ist denn bitte an throw; boese?


  • Mod

    Kellerautomat schrieb:

    Was ist denn bitte an throw; boese?

    Es ist halt ein Zeichen für einen Designfehler. Wenn man ein rethrow macht, dann hat man vorher höchstwahrscheinlich ein catch(...) gemacht. Das einzig sinnvolle, was man in einem catch(...) -Block machen kann, sind jedoch Aufräumarbeiten. Das wiederum bedeutet, dass man RAII nicht konsequent durchgezogen hat, denn dan wäre das unnötig. Und C++ ohne RAII ist aber nicht wirklich C++.
    Genau das ist in meinem Beispiel passiert: Zwar ist das gesamte Datenfeld geRAIIt (das ist ja gerade der Zweck der Klasse), aber sauber wäre es gewesen, so wie bei hustbaer auch die einzelnen Unterfelder mittels RAII zu verwalten, nicht nur das Gesamtobjekt. Dann entfallen nämlich die Aufräumarbeiten und die daraus folgende Codeduplikation in den Konstruktoren.

    Man sieht: Das faul programmierte Beispiel war anscheinen didaktisch wertvoll 😃 . Manchmal muss man eben auch mal demonstrieren, wie es falsch aussähe, damit klar wird, warum der richtige Weg besser ist.



  • Exceptions aus einem Thread entkommen lassen ist auch ein Designfehler. Was machst du da? :p



  • Kellerautomat schrieb:

    Exceptions aus einem Thread entkommen lassen ist auch ein Designfehler.

    Nicht wirklich.
    Wenn man die Exception nur mit "Mist, bitte 1x Programm abbrechen" behandeln kann, dann kann man sie auch gleich rausfliegen lassen. Hat sogar einige Vorteile.


Anmelden zum Antworten