Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » offsetof portabel, trotz Warnung?

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
26.06.2012, 13:47 Uhr
TOSHMAX



Folgendes Szenario:

C++:
#include <cstddef>
#include <iostream>

struct base {
    int n;
};

template<typename T>
struct container
    : public base
{
    T t;

    static container* from_T(T* t)
    {
        return reinterpret_cast<container*>(
                   reinterpret_cast<char*>(t)
                    - offsetof(container, t)    // 1.
                 // - sizeof(base)              // 2.
               );
    }
};

int main()
{
    container<int> c;
    c.t = 5;

    container<int>* c2 = container<int>::from_T(&c.t);

    std::cout << c2->t << std::endl;
    return 0;
}

T könnte auch eine Klasse mit virtuellen Methoden, also non-POD, sein.

Ich weiß, das ist nicht sonderlich schön, aber kann man eine der markierten Varianten gefahrlos verwenden und ist diese auch portabel einsetzbar?

Ich frage deshalb, weil mir der gcc für die Verwendung von offsetof eine Warnung ausgibt, aber alles was ich dazu Online finde, ergibt für mich keinen Sinn. Der Compiler muss hier doch genau sagen können, wo sich 't' im Vergleich zum 'container' befindet. Egal welcher Compiler oder welcher Typ T.

Im Voraus schon mal danke!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
26.06.2012, 23:07 Uhr
ao

(Operator)


Die Version 2 halte ich für gefährlich: Wenn zwischen base und t Padding-Bytes liegen, werden die nicht mitgezählt, und du rechnest falsch. Oder kann man das ausschließen?

Was für eine Warnung kommt denn bei der Version 1?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
26.06.2012, 23:13 Uhr
~f.-th.
Gast


Warnungen:

Code:
>g++ -pedantic -Os -o contain001z  contain001z.cpp -std=c++0x
contain001z.cpp: In instantiation of 'static container<T>* container<T>::from_T(T*) [with T = int; container<T> = container<int>]':
contain001z.cpp:32:42:   required from here
contain001z.cpp:23:16: warning: invalid access to non-static data member 'container<int>::t'  of NULL object [-Winvalid-offsetof]
contain001z.cpp:23:16: warning: (perhaps the 'offsetof' macro was used incorrectly) [-Winvalid-offsetof]
>Exit code: 0



Ein Digital Mars mit stl-port ohne Compilerschalter-Optimierung schluckt den Quelltext und gibt 5 aus.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
27.06.2012, 08:38 Uhr
ao

(Operator)


www.google.de/#q=invalid+access+to+non-static+data+member+of+null+object

Die ersten vier oder fünf Treffer erklären, was hier passiert. Ich habs überflogen, bin aber nicht tief eingedrungen. Kurze Zusammenfassung:

offsetof ist nur "sauber" definiert für sogenannte "POD structs" (Plain Old Data structures, anderes Wort für "C-Datenstrukturen"). Wenn man es auf Klassen loslässt, die vererbt sind, Konstruktoren haben usw. kann das wie gewohnt funktionieren, kann aber auch Ärger geben.

Der Grund: Wenn der C++-Standard vorschreiben würde, dass offsetof immer funktionieren muss, egal mit welchem Objekt, dann würde das die Compilerhersteller sehr einschränken in ihren Möglichkeiten, Klassen intern darzustellen. Es ist eine Abwägung, was wichtiger ist, und offsetof bis ins Letzte durchzuziehen hatte wohl nicht die hohe Priorität.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
27.06.2012, 08:39 Uhr
~f.-th.
Gast


Das Compilat des gcc gibt ebenfalls 5 aus.
Aber die Warnungen sollte man nicht einfach ignorieren.

Hier mal ein Link zu Infos mit Templates, Container und so:
www.cpp-entwicklung.de/cpplinux3/cpp_main/cpp_mainch4.html
In dem Beitrag ist wahrscheinlich noch nicht der aktuelle C++11 Standard berücksichtigt.

MfG f.-th.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
27.06.2012, 13:23 Uhr
TOSHMAX



base besteht in der richtigen Implementierung nur aus 2 Zeigern auf base. Padding sollte hier also kein Problem bereiten.

Ich habe die Suchtreffer bereits zuvor gefunden und gelesen und hier wird alles auch gut erklärt. Allerdings geht es dabei um virtuelle Vererbung, hier ist mir die Problematik auch klar. Aber ich sehe den Sinn nicht, warum der Compiler im Beispiel oben nicht sagen kann, wo sich t im Speicher befinden wird, obwohl ihm sein eigener Aufbau der Struktur ja klar sein muss.

Hier ist noch etwas, das der gcc zum Ignorieren der Warnung sagt:

Code:
-Wno-invalid-offsetof (C++ only)
    Suppress warnings from applying the `offsetof' macro to a non-POD type. According to the 1998 ISO C++ standard, applying `offsetof' to a non-POD type is undefined. In existing C++ implementations, however, `offsetof' typically gives meaningful results even when applied to certain kinds of non-POD types. (Such as a simple `struct' that fails to be a POD type only by virtue of having a constructor.) This flag is for users who are aware that they are writing nonportable code and who have deliberately chosen to ignore the warning about it.

    The restrictions on `offsetof' may be relaxed in a future version of the C++ standard.

Kann es sein, dass es sich hier um eine der Situationen handelt, bei denen "offsetof typically gives meaningful results" zutrifft?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
27.06.2012, 14:33 Uhr
ao

(Operator)



Zitat von TOSHMAX:
Kann es sein, dass es sich hier um eine der Situationen handelt, bei denen "offsetof typically gives meaningful results" zutrifft?

Exakt. Da man aber nicht weiß, durch welche Veränderungen an der Klasse das eventuell nicht mehr zutrifft (es handelt sich nun mal um undefiniertes Verhalten), ist hier große Vorsicht geboten.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
27.06.2012, 19:15 Uhr
~f.-th.
Gast


Hab noch nicht genug Plan bei dieser Baustelle.

Hier mal ein Code mit heisser Nadel:

C++:
#include <string>
#include <iostream>

struct base {
    int n;
};

template<typename T>
struct container
    : public base
{
    T t;

    static container* from_T(T t)    // t-format?
    {
        size_t found;
        std::string str("5153a");    // only demo

        found=str.find(t);    // ?
        if (found!=std::string::npos)
            std::cout << "found at: " << int(found) << std::endl;
        else
            std::cout << "not found" << std::endl;
        std::cout << t << "\n";
    }
};

int main()
{
    container<int> c;
    c.t = 'a';

    container<int>* c2 = container<int>::from_T(c.t);

    std::cout << c.t << std::endl;
    // std::cout << "c2: " << c2->t << std::endl;    // todo
    return 0;
}



Macht noch nicht ganz was du möchtest.

Also scheint der Compiler die Infos doch zu finden. Musst halt nur passend heraus bekommen.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
28.06.2012, 14:16 Uhr
TOSHMAX



Was soll denn der Code zeigen, außer wie man in einem std::string etwas sucht?
Kann es sein, dass hier etwas durcheinander geraten ist?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
28.06.2012, 22:38 Uhr
~f.-th.
Gast


Hier noch etwas zu offsetof und C++11:
http://en.cppreference.com/w/cpp/types/offsetof

Entspricht folgender Quelltext mehr deinen Vorstellungen:

C++:
#include <iostream>
#include <string>

//Diesmal drei Parameter, U ist per default gleich T, ebenso V
template <typename T, typename U=T, typename V=T>
struct Triple
{
  //drei Datenelemente
  T first;
  U second;
  V third;
  Triple(const T &a, const U &b, const V &c) : first(a), second(b), third(c) {}
  Triple(const Triple &t) : first(t.first), second(t.second), third(t.third) {}
  ~Triple() {}

  Triple& operator=(const Triple&);
};

//Definition außerhalb der Klasse:
template <typename T, typename U, typename V>
Triple<T,U,V>& Triple<T,U,V>::operator=(const Triple<T,U,V> &t)
{
  if (this == &t)
    return *this;

  first = t.first;
  second = t.second;
  third = t.third;
  return *this;
}

int main (int argc, char **argv)
{
  //ein Tripel vom Typ float:
  Triple<float> floatTriple(5.1,8.9,11.2);
  std::cout<<floatTriple.first<<'\t'<<floatTriple.second<<'\t'<<floatTriple.third<<'\n';

  //Erster Parameter ist ein string, der zweite ein int der dritte ein float:
  Triple<std::string, int, float> mixTriple("zwanzig", 20, 3.7);
  std::cout<<mixTriple.first<<'\t'<<mixTriple.second<<'\t'<<mixTriple.third<<'\n';
}



Das Thema Templates scheint nicht einfach. Wird in Netz oft heftig, teils widersprüchlich diskutiert.
Auch die Beispiele aus dem Netz laufen oft nicht auf Anhieb. Ursachen => Compilermarotten oder fehlerhafter Quellcode?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: