Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[rapidxml] błędne odczytywanie wartości węzła przy zbyt długim pliku

Ostatnio zmodyfikowano 2021-08-03 19:14
Autor Wiadomość
latajacaryba
Temat założony przez niniejszego użytkownika
[rapidxml] błędne odczytywanie wartości węzła przy zbyt długim pliku
» 2021-08-02 01:08:28
Witam,
mam problem z użyciem rapidxmla, mianowicie w pewnych sytuacjach (udało mi się jedynie ustalić, że przy pewnej długości pliku, a jest to paradoksalnie jedynie kilkanaście kb) wartości odczytywane z węzła są błędne. Nie jest to raczej wina błędnie sformatowanych danych, ponieważ plik składa się w głównej mierze z kilku powtarzających się "bloków" i nawet jeżeli są one takie same i odczyt poprzednich się udawał, to przy którymś jest w końcu problem.
Wartości kolejnych węzłów (pobieranych przez next_sibling()) są poprawne, aż do trafienia na któryś z kolei. Sprawdzałem w pliku i nie odbiega on od normy, a jak wspomniałem, w innych blokach jego wartość była poprawnie odczytywana.
przykład błędnie odczytanej wartości

<RockSimDocument>
    <FileVersion>4</FileVersion>
    <DesignInformation>
        <RocketDesign>
            <Name>Rakieta</Name>
            <StageCount>1</StageCount>
            <DisplayFlags>7</DisplayFlags>
            <ViewType>0</ViewType>
            <ViewStageCount>3</ViewStageCount>
            <ViewTypeEdit>0</ViewTypeEdit>
            <ViewStageCountEdit>3</ViewStageCountEdit>
            <ZoomFactor>0.0</ZoomFactor>
            <ZoomFactorEdit>0.0</ZoomFactorEdit>
            <ScrollPosX>0</ScrollPosX>
            <ScrollPosY>0</ScrollPosY>
            <ScrollPosXEdit>0</ScrollPosXEdit>
            <ScrollPosYEdit>0</ScrollPosYEdit>
            <ThreeDFlags>0</ThreeDFlags>
            <ThreeDFlagsEdit>0</ThreeDFlagsEdit>
            <LastSerialNumber>8</LastSerialNumber>
            <Stage3Mass>0.0</Stage3Mass>
            <Stage2Mass>0.0</Stage2Mass>
            <Stage1Mass>0.0</Stage1Mass>
            <Stage3CG>417.5471832763761</Stage3CG>
            <Stage2CGAlone>0.0</Stage2CGAlone>
            <Stage1CGAlone>0.0</Stage1CGAlone>
            <Stage321CG>0.0</Stage321CG>
            <Stage32CG>0.0</Stage32CG>
            <CPCalcFlags>1</CPCalcFlags>
            <CPSimFlags>1</CPSimFlags>
            <UseKnownMass>0</UseKnownMass>
            <Stage3Parts>
                <NoseCone>
                    <KnownMass>14.961414816532843</KnownMass>
                    <Density>680.0</Density>
                    <Material>Karton</Material>
                    <Name>Głowica</Name>
                    <KnownCG>0.0</KnownCG>
                    <UseKnownCG>0</UseKnownCG>
                    <Xb>0.0</Xb>
                    <CalcMass>14.961414816532843</CalcMass>
                    <CalcCG>101.96760546415946</CalcCG>
                    <DensityType>0</DensityType>
                    <RadialLoc>0.0</RadialLoc>
                    <RadialAngle>0.0</RadialAngle>
                    <LocationMode>0</LocationMode>
                    <Len>150.00000000000003</Len>
                    <FinishCode>2</FinishCode>
                    <SerialNo>0</SerialNo>
                    <ShapeCode>0</ShapeCode>
                    <ConstructionType>1</ConstructionType>
                    <WallThickness>2.0</WallThickness>
                    <ShapeParameter>0.0</ShapeParameter>
                    <AttachedParts/>
                    <BaseDia>50.0</BaseDia>
                    <ShoulderLen>0.0</ShoulderLen>
                    <ShoulderOD>0.0</ShoulderOD>
                </NoseCone>
                <BodyTube>
                    <KnownMass>41.016633685268374</KnownMass>
                    <Density>680.0</Density>
                    <Material>Karton</Material>
                    <Name>Korpus rakiety</Name>
                    <KnownCG>0.0</KnownCG>
                    <UseKnownCG>0</UseKnownCG>
                    <Xb>0.0</Xb>
                    <CalcMass>41.016633685268374</CalcMass>
                    <CalcCG>100.0</CalcCG>
                    <DensityType>0</DensityType>
                    <RadialLoc>0.0</RadialLoc>
                    <RadialAngle>0.0</RadialAngle>
                    <LocationMode>0</LocationMode>
                    <Len>200.0</Len>
                    <FinishCode>2</FinishCode>
                    <SerialNo>1</SerialNo>
                    <OD>50.0</OD>
                    <ID>46.0</ID>
                    <IsMotorMount>0</IsMotorMount>
                    <MotorDia>46.0</MotorDia>
                    <EngineOverhang>0.0</EngineOverhang>
                    <IsInsideTube>0</IsInsideTube>
                    <AttachedParts/>
                </BodyTube>
                                           Tutaj jest 7 identycznych węzłów <BodyTube> jak ten wyżej, stąd pominę ich dołączanie
            </Stage3Parts>
            <Stage2Parts/>
            <Stage1Parts/>
        </RocketDesign>
    </DesignInformation>
</RockSimDocument>
P-178810
pekfos
» 2021-08-02 06:49:06
Masz błąd w kodzie, więc podaj jakiś kod.
P-178811
latajacaryba
Temat założony przez niniejszego użytkownika
» 2021-08-03 03:50:45
parsowanie i przesuwanie się po węzłach
C/C++
rapidxml::xml_document < > xmlDoc;
try
{
   
xmlDoc.parse < 0 >( filedata.get() );
}
catch( const rapidxml::parse_error & e )
{
   
throw e;
}
currentElementNode = xmlDoc.first_node();
currentElementNode = currentElementNode->first_node( "DesignInformation" );
currentElementNode = currentElementNode->first_node( "RocketDesign" );
currentElementNode = currentElementNode->first_node( "Stage3Parts" );

Funkcje odpowiadające za odczytywanie wartości
C/C++
template < typename T >
void RktReader::getNodeValue( const rapidxml::xml_node < > * const node, std::string_view nodeName, T & variable )
{
   
auto * found = node->first_node( nodeName.data() );
   
if( found == nullptr )
       
 return;
   
   
const std::string_view view = found->value();
   
std::from_chars( view.data(), view.data() + view.size(), variable );
}

template < >
inline void RktReader::getNodeValue < std::string >( const rapidxml::xml_node < > * const node, std::string_view nodeName, std::string & variable )
{
   
auto * found = node->first_node( nodeName.data() );
   
variable = found->value();
}

Powyższe wykorzystuje ta funkcja oraz jej przeciążone w podklasach wersje

C/C++
void Element::Load( rapidxml::xml_node < > * node )
{
   
RktReader & ref = RktReader::getInstance();
   
ref.getNodeValue( node, "KnownMass", knownMass );
   
ref.getNodeValue( node, "Density", density );
   
ref.getNodeValue( node, "Material", material );
   
ref.getNodeValue( node, "Name", name );
   
ref.getNodeValue( node, "Len", length );
}

Ogólny schemat:
C/C++
// na tym etapie node typu xml_node<>* wskazuje pierwszy element Stage3Parts (w powyższym przypadku jest to NoseCone)
while( node != nullptr )
{
   
std::shared_ptr < Element > newElem = nullptr;
   
std::string_view elementName = node->name();
   
// kod niepowiązany z działaniem rapidxml
   
newElem->Generate( node );
   
node = node->next_sibling();
}

Generate jest metodą w której wywoływane jest wspomniane wyżej Load()

Czasami wrzuca wyjątkiem, a innym razem (rzadziej) wykonuje się assert. Przed chwilą zaś pierwszy raz zadziałało, ale znów dzieje się to samo.
assert
exception

EDIT: błąd pojawia się przy
auto * found = node->first_node( nodeName.data() );
 w getNodeValue() a konkretniej patrząc na call stack, to:
getNodeValue >> first_node >> name_size
P-178812
pekfos
» 2021-08-03 07:24:37
Najlepiej kompilujący się kod.
» Kurs C++» FAQJak wstawiać kod na forum? pytanie/odpowiedź
W tym kodzie na razie nic się nie rzuca w oczy, poza tym że używasz std::string_view w niebezpieczny sposób.
C/C++
auto * found = node->first_node( nodeName.data() ); // choćby tu
P-178813
latajacaryba
Temat założony przez niniejszego użytkownika
» 2021-08-03 17:15:20
Kod sklada się z kilkudziesięciu plików, externali i ciężko byłoby go tu wrzucić, ale to nic, bo już wiem w czym był problem.
Pierwszy wrzucony fragment kodu (jego część):
C/C++
rapidxml::xml_document < > xmlDoc;

obiekt jest lokalny, a currentElementNode odnosi się do nieistniejącego potem obiektu. Głupia rzecz a jednak. Co prawda nadal nie jestem pewien, dlaczego dla mniejszych plików wszystko działało, ale wygląda na to, że po wydłużeniu czasu życia xmlDoc wszystko działa.

Z ciekawości, dlaczego w niebezpieczny sposób?
P-178816
pekfos
» 2021-08-03 17:54:31
obiekt jest lokalny, a currentElementNode odnosi się do nieistniejącego potem obiektu.
Czyli jak zwykle problem był w niepodanym fragmencie kodu.

Co prawda nadal nie jestem pewien, dlaczego dla mniejszych plików wszystko działało
Kwestia ilości używanej pamięci. Dla mniejszych plików po prostu trudniej zreprodukować problem, dlatego "działało".

Z ciekawości, dlaczego w niebezpieczny sposób?
Zakładasz że string view wskazuje na napis zakończony zerem, a cały sens używania string view jest w tym, by napisy nie musiały takie być. Jak chcesz robić oszczędności, to już lepiej przekaż ten napis przez wskaźnik, wtedy wiadomo czego się po tym spodziewać i to jest też forma jakiej potrzebujesz w implementacji.
C/C++
const std::string_view view = found->value();
std::from_chars( view.data(), view.data() + view.size(), variable );
Skoro już mowa o oszczędnościach, to rapidxml pozwala pobrać długość napisu, więc string view jest zbędne, zwłaszcza że musi samemu obliczyć tą długość.
P-178817
latajacaryba
Temat założony przez niniejszego użytkownika
» 2021-08-03 19:14:37
Ok, dzięki za pomoc i wyjaśnienia, zamykam
P-178818
« 1 »
  Strona 1 z 1