istream не полностью восстанавливает то, что было помещено в stringstream - PullRequest
4 голосов
/ 12 июня 2019

Я использую следующую настройку:

#include <bits/stdc++.h>
using namespace std;

class foo {
    public:
        void bar( istream &in, int n ) {
            vector<tuple<int,int,int,int>> q;
            int x,y,a,b;
            for ( q.clear(); in >> x >> y >> a >> b; q.push_back(make_tuple(x,y,a,b)) );
            assert( n == q.size() );
        }
};

int main() {
    stringstream ss;
    for ( int i= 0; i < 100; ++i )
        ss << rand() << " " << rand() << " " << rand() << " " << rand() << endl;
    ss.clear(), ss.seekg(0,std::ios::beg);
    (new foo())->bar(ss,100);
}

На самом деле мой код более сложен, чем этот, но идея в том, что я помещаю вещи (точнее long long int s) в stringstream и вызываю функцию, предоставляя созданный stringstream как istream объект. Приведенный выше пример работает нормально, но в моем конкретном случае я поставил, скажем, 2 mln кортежи. И проблема в том, что числа не полностью восстановлены на другом конце, внутри foo (я получаю меньше 2000000 чисел). Можете ли вы представить сценарий, когда это может произойти? Может ли это in >> x >> y >> a >> b как-то закончиться раньше, чем будет исчерпан ввод?

РЕДАКТИРОВАТЬ: Я использовал этот чек:

if ( ss.rdstate() and std::stringstream::badbit ) {
    std::cerr << "Problem in putting stuff into stringstream!\n";
    assert( false );
}

Каким-то образом все проходили эту проверку.

РЕДАКТИРОВАТЬ: Как я уже сказал, я проверяю работоспособность внутри main(), восстанавливая входные номера с помощью >> -метода и действительно возвращая 2 mln (кортежи) чисел. Когда объект stringstream передается в foo, он восстанавливает только часть чисел, а не все.

РЕДАКТИРОВАТЬ: Для чего это стоит, я вставляю фактический контекст здесь. Из-за его зависимостей он не будет компилироваться, но, по крайней мере, мы сможем увидеть ошибочные строки. Это метод run(), который не может восстановить запросы, предоставленные методом main().

#include <iostream>
#include <algorithm>
#include <chrono>

const unsigned long long PERIOD= 0x1full;

class ExpRunnerJSONOutput : public ExperimentRunner {
    std::string answers;
    void set_name( std::string x ) {
        this->answers= "answers."+x+".txt";
    }
public:
    ExpRunnerJSONOutput( query_processor *p ) : ExperimentRunner(p) {
        set_name(p->method_name);
    }

    ExperimentRunner *setProcessor( query_processor *p) override {
        ExperimentRunner::setProcessor(p);
        set_name(p->method_name);
        return this;
    }

    // in: the stream of queries
    // out: where to write the results to
    virtual void run( std::istream &in, std::ostream &out ) override {

        node_type x,y;
        value_type a,b;
        unsigned long long i,j,rep_period= (16383+1)*2-1;

        auto n= tree->size();

        std::vector<std::tuple<node_type,node_type,value_type,value_type>> queries;
        for ( queries.clear(); in >> x >> y >> a >> b; queries.push_back(std::make_tuple(x,y,a,b)) ) ;

        value_type *results= new value_type[queries.size()], *ptr= results;

        /* results are stored in JSON */
        nlohmann::json sel;

        long double total_elapsed_time= 0.00;
        std::chrono::time_point<std::chrono::high_resolution_clock,std::chrono::nanoseconds> start, finish;
        long long int nq= 0, it= 0;

        start= std::chrono::high_resolution_clock::now();
        int batch= 0;
        for ( auto qr: queries ) {
            x= std::get<0>(qr), y= std::get<1>(qr);
            a= std::get<2>(qr), b= std::get<3>(qr);
            auto ans= processor->count(x,y,a,b); nq+= ans, nq-= ans, ++nq, *ptr++= ans;
        }
        finish = std::chrono::high_resolution_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(finish-start);
        total_elapsed_time= elapsed.count();
        sel["avgtime_microsec"]= total_elapsed_time/nq*(1e-3);

        out << sel << std::endl;
        out.flush();

        delete[] results;

    }
    ~ExpRunnerJSONOutput() final {}
};

void runall( std::istream &in, char *res_file, ExpRunnerJSONOutput *er ) {
    in.clear(), in.seekg(0,std::ios::beg);
    std::string results_file= std::string(res_file);
    std::ofstream out;
    try {
        out.open(results_file,std::ios::app);
    }
    catch ( std::exception &e ) {
        throw e;
    }
    er->run(in,out), out.close();
}

using instant= std::chrono::time_point<std::chrono::steady_clock,std::chrono::nanoseconds>;

void sanity_check( std::istream &in, size_type nq ) {
    node_type x,y;
    value_type a,b;
    size_type r= 0;
    for ( ;in >> x >> y >> a >> b; ++r ) ;
    assert( r == nq );
}

int main( int argc, char **argv ) {
    if ( argc < 5 ) {
        fprintf(stderr,"usage: ./<this_executable_name> <dataset_name> <num_queries> <result_file> K");
        fflush(stderr);
        return 1;
    }
    query_processor *processor;
    std::string dataset_name= std::string(argv[1]);
    auto num_queries= std::strtol(argv[2],nullptr,10);
    auto K= std::strtol(argv[4],nullptr,10);
    std::ifstream in;
    std::ofstream logs;
    try {
        in.open(dataset_name+".puu");
        logs.open(dataset_name+".log");
    } catch ( std::exception &e ) {
        throw e;
    }
    std::string s; in >> s;
    std::vector<pq_types::value_type> w;
    w.clear();
    pq_types::value_type maxw= 0;
    for ( auto l= 0; l < s.size()/2; ++l ) {
        value_type entry;
        in >> entry;
        w.emplace_back(entry);
        maxw= std::max(maxw,entry);
    }
    in.close();

    const rlim_t kStackSize= s.size()*2;
    struct rlimit r1{};
    int result= getrlimit(RLIMIT_STACK,&r1);
    if ( result == 0 ) {
        if ( r1.rlim_cur < kStackSize ) {
            r1.rlim_cur= kStackSize;
            result= setrlimit(RLIMIT_STACK,&r1);
            if ( result != 0 ) {
                logs << "setrlimit returned result = " << result << std::endl;
                assert( false );
            }
        }
    }
    logs << "stack limit successfully set" << std::endl;

    instant start, finish;

    remove(argv[3]);

    auto sz= s.size()/2;
    random1d_interval_generator<> rig(0,sz-1), wrig(0,maxw);

    auto node_queries= rig(num_queries), weight_queries= wrig(num_queries,K);
    assert( node_queries.size() == num_queries );
    assert( weight_queries.size() == num_queries );
    std::stringstream ss;
    ss.clear(), ss.seekg(0,std::ios::beg);
    for ( int i= 0; i < num_queries; ++i )
        ss << node_queries[i].first << " " << node_queries[i].second << " " << weight_queries[i].first << " " << weight_queries[i].second << "\n";
    ss.clear(), ss.seekg(0,std::ios::beg);
    sanity_check(ss,num_queries);

    start = std::chrono::steady_clock::now();
    auto *er= new ExpRunnerJSONOutput(processor= new my_processor(s,w,dataset_name));
    finish = std::chrono::steady_clock::now();
    logit(logs,processor,start,finish);
    runall(ss,argv[3],er), delete processor;

    logs.close();

    return 0;
}

EDIT: мне было интересно, связано ли это с ifstream.eof () - конец файла достигнут до реального конца Теперь, как подтвердить гипотезу - чтение прекращается, когда мы достигаем байта со значением 26?

РЕДАКТИРОВАТЬ: еще одно обновление. После прочтения вещей внутри foo, rdstate() вернул 4, fail() == 1 и eof() == 0. Таким образом, очевидно, что конец файла не был достигнут.

Ответы [ 2 ]

1 голос
/ 12 июня 2019

Вы не проверяете состояние своего потока. Существует верхний предел того, сколько вы можете вписать туда - в основном, максимальный размер строки. Это подробно обсуждается в этом вопросе

Проверить наличие ошибок при записи в поток строк?

stringstream ss;
for (int i = 0; i < 100000000; ++i) //or some other massive number?
{
    ss << rand() << " " << rand() << " " << rand() << "  " << rand() << endl;
    if (ss.rdstate() & stringstream::badbit)
        std::cerr << "Problem!\n";
}

Вы можете хотите проверить конкретные записи чисел.

0 голосов
/ 13 июня 2019

В конечном счете, я использовал старый добрый FILE * вместо istream с, и все работало, как ожидалось. По какой-то причине последний считывал только часть файла (а именно, его префикс) и преждевременно останавливался с истинным значением fail(). Понятия не имею почему.

...