Сбой при использовании time.h

==ПОСЛЕДНИЙ КЛАСС ПОД ЭТОЙ ЛИНИЕЙ==

Это не было проблемой с std::ostringstream, я делал плохие вещи с "time.h", которые я не совсем понимал. Исходный вопрос появляется после урока. Окончательный класс выглядит так:

отметка времени.ч

#ifndef __TIMESTAMP_H
#define __TIMESTAMP_H

#include <string>
#include "time.h"


class CTimestamp {

    private:
        std::string timestamp;
        time_t rawtime;
        struct tm errorTime;
        struct tm tempTime;
        bool quality;

    public:
        CTimestamp();
        void set(std::string inputTime);
        std::string get();
        std::string get(int modifiedBy);
        bool good();

        void getWeekday();  // Mainly for testing purposes - if this returns
                            // the correct weekday for your modified timestamp,
                            // you probably modified it correctly.
};

#endif

timestamp.cpp

#include "timestamp.h"
#include "time.h"
#include <string>
#include <stdlib.h>
#include <iostream>
#include <sstream>

CTimestamp::CTimestamp(){

    quality = 0;
}

void CTimestamp::set(std::string inputTime){

    quality = 1;

    int year, month, day, hour, minute, second;

    if (19 == inputTime.length()){

        inputTime.replace(10,1," ");
        inputTime.replace(13,1,":");
        inputTime.replace(16,1,":");

        year = atoi(inputTime.substr(0,4).c_str());
        month = atoi(inputTime.substr(5,2).c_str());
        day = atoi(inputTime.substr(8,2).c_str());
        hour = atoi(inputTime.substr(11,2).c_str());
        minute = atoi(inputTime.substr(14,2).c_str());         
        second = atoi(inputTime.substr(17,2).c_str());

        timestamp = inputTime;
    }
    else{
        quality = 0;
    }

    if(quality){
        // Get current time with the "time_t time(struct tm * timeptr)" function from time.h
        time(&rawtime);

        // Change to local time with "struct tm * localtime (const time_t * timer)" function from time.h
        errorTime = *localtime(&rawtime);

        // Change to the time of the timestamp
        errorTime.tm_year = year - 1900;   //Years since 1900
        errorTime.tm_mon  = month - 1;     //Months since january
        errorTime.tm_mday = day;           //Day of the month
        errorTime.tm_hour = hour;      //Hours since midnight
        errorTime.tm_min  = minute;        //minutes since hour
        errorTime.tm_sec  = second;        //Seconds since minute

        // modifies errorTime so overflows in lower units increment higher units then sets tm_wday and tm_yday
        mktime ( &errorTime );
    }
}

void CTimestamp::getWeekday(){

    const char * weekday[] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
    std::cout << weekday[errorTime.tm_wday];
}

std::string CTimestamp::get(){
    std::string returnValue = "Bad Initialization";
    if(quality){
        returnValue = timestamp;
    }
    return returnValue;
}

std::string CTimestamp::get(int modifiedBy){

    std::string returnValue = "Bad Initialization";
    if(quality){

        tempTime = errorTime;
        tempTime.tm_sec = (errorTime.tm_sec+modifiedBy);
        mktime( &tempTime);

        std::string year, month, day, hour, minute, second;

        // This compiler does not support the C++11 std::to_string but there is a workaround with stringstreams
        // http://www.cplusplus.com/articles/D9j2Nwbp/
        year   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_year+1900)) )->str();
        month  = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_mon+1)) )->str();
        day    = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_mday)) )->str();
        hour   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_hour)) )->str();
        minute = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_min)) )->str();
        second = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime.tm_sec)) )->str();

        if(month.length()  == 1) { month  = "0" + month;  }
        if(day.length()    == 1) { day    = "0" + day;    }
        if(hour.length()   == 1) { hour   = "0" + hour;   }
        if(minute.length() == 1) { minute = "0" + minute; }
        if(second.length() == 1) { second = "0" + second; }

        returnValue = year+"-"+month+"-"+day+" "+hour+":"+minute+":"+second;
    }

    return returnValue;
}

bool CTimestamp::good(){

    return quality;
}

==ИСХОДНЫЙ ВОПРОС НАЧИНАЕТСЯ ЗДЕСЬ ПОД ЭТОЙ ЛИНИЕЙ==

Мне нужна некоторая функциональность временной метки, и я пытаюсь создать для нее класс. К сожалению, моя программа вылетает, когда я пытаюсь вызвать одну из функций, и я не совсем понимаю, почему, особенно в свете того факта, что она НЕ вылетает, когда я использую ее в небольшой игрушечной программе для тестирования класса.

#ifndef __TIMESTAMP_H
#define __TIMESTAMP_H

#include <string>
#include "time.h"
#include "debug.h"

class CTimestamp {

    private:
        std::string timestamp;
        time_t rawtime;
        struct tm * errorTime;
        struct tm * tempTime;
        bool quality;

    public:
        CTimestamp();
        void set(std::string inputTime);
        std::string get();
        std::string get(int modifiedBy);
        bool good();

        void getWeekday();  // Mainly for testing purposes - if this returns
                            // the correct weekday for your modified timestamp,
                            // you probably modified it correctly.
};

#endif

Проблема возникает, когда я звоню

std::cout << timeStamp.get(-30);

В частности, в этот момент:

year   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_year+1900)) )->str();

Что является частью следующего метода:

std::string CTimestamp::get(int modifiedBy){
    if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    std::string returnValue = "Bad Initialization";
    if(quality){

        tempTime->tm_year  = errorTime->tm_year;
        tempTime->tm_mon   = errorTime->tm_mon;
        tempTime->tm_mday  = errorTime->tm_mday;
        tempTime->tm_hour  = errorTime->tm_hour;
        tempTime->tm_min   = errorTime->tm_min;
        tempTime->tm_sec   = errorTime->tm_sec;
        mktime(tempTime);

        tempTime->tm_sec = tempTime->tm_sec + modifiedBy;
        mktime(tempTime);

        std::string year, month, day, hour, minute, second;

        if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

        // This compiler does not support the C++11 std::to_string but there is a workaround with stringstreams
        // http://www.cplusplus.com/articles/D9j2Nwbp/
        year   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_year+1900)) )->str();
        month  = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_mon+1)) )->str();
        day    = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_mday)) )->str();
        hour   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_hour)) )->str();
        minute = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_min)) )->str();
        second = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_sec)) )->str();

        if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

        if(month.length() == 1)
        {
            month = "0" + month;
        }
        if(day.length() == 1)
        {
            day = "0" + day;
        }
        if(hour.length() == 1)
        {
            hour = "0" + hour;
        }
        if(minute.length() == 1)
        {
            minute = "0" + minute;
        }
        if(second.length() == 1)
        {
            second = "0" + second;
        }

        if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

        returnValue = year+"-"+month+"-"+day+" "+hour+":"+minute+":"+second;

        if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    }

    if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    return returnValue;
}

ИЗМЕНИТЬ

Хорошо, это меня смущает:

Когда я звоню

std::cout << timeStamp.get();

он вылетает на линии, которую я указал.

Когда я вместо этого иду

std::string hey = timeStamp.get();
std::cout << hey;

он падает на втором операторе __DEBUG__ (сразу после if(quality))

ИЗМЕНИТЬ ИЗМЕНИТЬ

errorTime инициализируется здесь:

void CTimestamp::set(std::string inputTime){

    quality = 1;

    int year, month, day, hour, minute, second;

    if (19 == inputTime.length()){

        inputTime.replace(10,1," ");
        inputTime.replace(13,1,":");
        inputTime.replace(16,1,":");

        year = atoi(inputTime.substr(0,4).c_str());
        month = atoi(inputTime.substr(5,2).c_str());
        day = atoi(inputTime.substr(8,2).c_str());
        hour = atoi(inputTime.substr(11,2).c_str());
        minute = atoi(inputTime.substr(14,2).c_str());         
        second = atoi(inputTime.substr(17,2).c_str());

        timestamp = inputTime;
    }
    else{
        quality = 0;
    }

    if(quality){
        // Get current time with the "time_t time(struct tm * timeptr)" function from time.h
        time(&rawtime);

        // Change to local time
        errorTime = localtime(&rawtime);

        // Change to the time of the timestamp
        errorTime->tm_year = year - 1900;   //Years since 1900
        errorTime->tm_mon  = month - 1;     //Months since january
        errorTime->tm_mday = day;           //Day of the month
        errorTime->tm_hour = hour;          //Hours since midnight
        errorTime->tm_min  = minute;        //minutes since hour
        errorTime->tm_sec  = second;        //Seconds since minute

        // modifies errorTime so overflows in lower units increment higher units then sets tm_wday and tm_yday
        mktime ( errorTime );
    }
}

ИЗМЕНИТЬ ИЗМЕНИТЬ ИЗМЕНИТЬ

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

    /*
    year   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_year+1900)) )->str();
    month  = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_mon+1)) )->str();
    day    = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_mday)) )->str();
    hour   = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_hour)) )->str();
    minute = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_min)) )->str();
    second = static_cast<std::ostringstream*>( &(std::ostringstream() << (tempTime->tm_sec)) )->str();
    */

    int timeConvertINT;
    std::ostringstream timeConvertOSS;

    timeConvertINT = (tempTime->tm_year)+1900;
    timeConvertOSS << timeConvertINT;
    year = timeConvertOSS.str();

    timeConvertINT = (tempTime->tm_mon)+1;
    timeConvertOSS << timeConvertINT;
    month = timeConvertOSS.str();

    timeConvertINT = (tempTime->tm_mday);
    timeConvertOSS << timeConvertINT;
    day = timeConvertOSS.str();

    timeConvertINT = (tempTime->tm_hour);
    timeConvertOSS << timeConvertINT;
    hour = timeConvertOSS.str();

    timeConvertINT = (tempTime->tm_min);
    timeConvertOSS << timeConvertINT;
    minute = timeConvertOSS.str();

    timeConvertINT = (tempTime->tm_sec);
    timeConvertOSS << timeConvertINT;
    second = timeConvertOSS.str();

ИЗМЕНИТЬ ИЗМЕНИТЬ ИЗМЕНИТЬ ИЗМЕНИТЬ

Хм. Таким образом, кажется, что ostreamstring здесь НЕ является нарушителем - он падает именно на этой строке, даже когда я комментирую все функции OSS и просто жестко закодирую ответ.

Это означает, что в этом блоке:

if(quality){

    if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    tempTime->tm_year  = errorTime->tm_year;
    tempTime->tm_mon   = errorTime->tm_mon;
    tempTime->tm_mday  = errorTime->tm_mday;
    tempTime->tm_hour  = errorTime->tm_hour;
    tempTime->tm_min   = errorTime->tm_min;
    tempTime->tm_sec   = errorTime->tm_sec;
    mktime(tempTime);

    if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    tempTime->tm_sec = tempTime->tm_sec + modifiedBy;
    mktime(tempTime);

    std::string year, month, day, hour, minute, second;

    if ( 1 < __DEBUG__ ){std::cout << "\nDEBUG " << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ ;}

    year = "2013";
    month = "11";
    day = "05";
    hour = "12";
    minute = "00";
    second = "00";

Я вижу первый вывод отладки (и ни один позже) при вызове его как

std::string hey = timeStamp.get();
std::cout << hey;

Но когда я называю это как

std::cout << timeStamp.get();

Я вижу вывод отладки сразу после

std::string year, month, day, hour, minute, second;

person medivh    schedule 17.12.2013    source источник
comment
Какой компилятор? Потому что даже VS поддерживает std::to_string до VS2010.   -  person Mgetz    schedule 17.12.2013
comment
g++ в MINGW - это (старая) известная проблема.   -  person medivh    schedule 17.12.2013
comment
Где в вашем коде вы инициализируете tempTime и errorTime? На что они указывают?   -  person Blastfurnace    schedule 17.12.2013
comment
@medivh эта ошибка, кажется, исправлена Я не знаю, если хотя исправление уже выпущено   -  person Mgetz    schedule 17.12.2013
comment
@Mgetz Нет - первое, что я сделал после того, как узнал об исправлении, это обновил MINGW, но, к сожалению, это не помогло.   -  person medivh    schedule 17.12.2013
comment
Хорошо, а где вы инициализировали tempTime? Похоже, вы разыменовываете неинициализированный указатель.   -  person Blastfurnace    schedule 17.12.2013
comment
@Blastfurnace Возможно, я ошибаюсь, но... сразу после if(quality)? Или я совершенно ошибаюсь в этом? (Если это окажется настоящей ошибкой, я буду очень смущен, но в свою защиту скажу, что это действительно работало в игрушечной программе, в которой я ее тестировал)   -  person medivh    schedule 17.12.2013
comment
@Blastfurnace ну, это было прямо смущающим, явно инициализируя tempTime перед записью в него значений, решило мою проблему. Спасибо. - РЕДАКТИРОВАТЬ: интересно, почему это сработало в моей игрушечной программе, а не здесь (у меня есть предположение: структура памяти, но нет уверенности)   -  person medivh    schedule 17.12.2013
comment
Да, разыменование неинициализированного указателя - это неопределенное поведение (включая видимость работы). Не возражаете, если я опубликую ответ, чтобы получить приятные очки репутации?   -  person Blastfurnace    schedule 17.12.2013
comment
Давай, не то чтобы я ничего не должен.   -  person medivh    schedule 17.12.2013


Ответы (2)


Таинственные сбои и указатели идут вместе, поэтому убедитесь, что tempTime и errorTime указывают на действительные объекты, прежде чем разыменовывать их. Опубликованный код не показывает, где вы инициализируете tempTime, так что именно с этого нужно начинать поиск.

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

person Blastfurnace    schedule 17.12.2013
comment
Область действия у меня под контролем, но да, я забыл явно инициализировать tempTime - person medivh; 17.12.2013
comment
Исправление: я совершенно неправильно понял библиотеку time.h самым яростным образом - errorTime и tempTime указывали на одну и ту же структуру данных. - person medivh; 17.12.2013

Код "кажется" законным, но формально - на самом деле он некорректен, потому что вы берете адрес временного. &(std::ostringstream() << (tempTime->tm_year+1900)).

Вы можете легко избежать этого - просто напишите (std::ostringstream() << (tempTime->tm_year+1900)).str(). Если он все еще падает, попробуйте дать std::ostringstream() имя, например

std::ostringstream() oss;
oss << (tempTime->tm_year+1900);
oss.str();

Если и это не поможет - у меня пока нет идей.

person Sergei Nosov    schedule 17.12.2013
comment
Я попробую старый добрый колледж и сообщу, исправит ли он что-нибудь. РЕДАКТИРОВАТЬ: Подождите, что именно заменить? Я не уверен, какие части вы удалили для краткости и какие части вы удалили для функциональности - person medivh; 17.12.2013
comment
Он не берет адрес временного. Вернее, нет ничего незаконного в том, чтобы взять адрес временного; незаконным является применение (встроенного) оператора & к rvalue. Чего он не делает. (Конечно, вы должны уважать время жизни объекта и не разыменовывать указатель после того, как объект перестал существовать. Но здесь это не проблема.) - person James Kanze; 17.12.2013
comment
Что ж, &(std::ostringstream()) в точности применяет оператор & к rvalue. Я ошибаюсь? Я так не думаю. Соглашусь с вами насчет времени жизни, что это вроде бы не проблема, но еще раз - формально это не корректно, так что могут быть проблемы (а могут и не быть). - person Sergei Nosov; 17.12.2013