-1

I'm doing a simulation of a few Ising models (lattice with 1 or -1) and I don't want to use files (don't gonna get into details here, I guess :D). So, the thing is, I'm using stringstreams to format the data & data's filename to then separate them with bash. Show you an example code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>    
#include <iostream>
#include <sstream>
#include <string>    
using namespace std;
void print2bash(string file, string data);

int main(int argc, char *argv[]){

    int i, j, n = atoi(argv[1]);
    // I want to put this outside main() {
    stringstream ssbash;    ssbash.clear(); ssbash.str(""); 
    stringstream ssfile;    ssfile.clear(); ssfile.str("");
    //} 
    //  So I don't have to do ss.clear and ss.str("") every time before using them

    ssfile << "this_" << n << "_is_the_filename.dat" << endl;
    for(i = 0; i < n ; i++){
        for(j = 0; j < n; j++)
            ssbash << i*n+j << "\t";
        ssbash << endl;
    }

    print2bash( ssfile.str(), ssbash.str() );

    return 0;
}
// print2bash(); prints string data; to string file; 
//               This will be used to separate data 
//               afterwards ( i.e. ./Ising.o <arguments> | bash )  
void print2bash(string file, string data){

    stringstream ssbash;    ssbash.clear(); ssbash.str(data);       
    stringstream output;    output.clear(); output.str("");
    string line;

    output << "for((i=0;i<1;i++)) do "<< endl; // Buffer all to bash at once
    while( getline(ssbash,line) )
        output << "    echo \"" << line << "\";" << endl;

    output << "done >> " << file; // Appending
    cout << output.str() << endl;

    ssbash.clear(); ssbash.str("");

    return ;
}

A regular output of this "sstreams.o" program would be

~work/Ising$ ./sstreams.o 5
for((i=0;i<1;i++)) do 
    echo "0 1 2 3 4 ";
    echo "5 6 7 8 9 ";
    echo "10 11 12 13 14 ";
    echo "15 16 17 18 19 ";
    echo "20 21 22 23 24 ";
done >> this_5_is_the_filename.dat

You get the idea right? From this thinking flow, what I actually write is ./sstreams.o 10 | bash so one gets nicely separated files. But the problem is:

I'm getting tired of writing this lines each time I want to use print2bash() (oh the irony!)

stringstream ssbash;    ssbash.clear(); ssbash.str(""); 
stringstream ssfile;    ssfile.clear(); ssfile.str("");

Because of that, I want to put the ss as global variables (don't know if this is that kind of awful so the compiler doesn't let me to do it: I'm just a Physicist). So, even if I write std::stringstream for the ss variables after the #includes (and using namespace std), I still get the error

error: ‘ssbash’ does not name a type
error: ‘ssbash’ does not name a type
error: ‘ssfile’ does not name a type
error: ‘ssfile’ does not name a type

from the compiler. Hope you can help meee.


EDIT: [SOLVED]

The problem was that if I wanted to declare a stringstream out of main() it failed because of the members clear & str where out of context so they can't be used. Besides that, this use was unnecessary because stringstreams are created with empty contents, as @jpalecek and @umlaeute pointed out.

I've declared the stringstreams out of main and include a clear() + str("") on print2bash() to give the desired result:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>    
#include <iostream>
#include <sstream>
#include <string> 
#include <vector>

using namespace std;

stringstream ssbash, ssfile;

void print2bash(string file, string data);

int main(int argc, char *argv[]){

    int i, j, n = atoi(argv[1]), p, np = atoi(argv[2]);
    vector< vector<int> > chain;    chain.resize(n*n);

    for( p = 0; p < np; p++){

        ssfile << "chain_p="<< p << "_filename.dat" << endl;
        for(i = 0; i < n ; i++){
            for(j = 0; j < n; j++){

                chain[p].push_back( i*n + j );
                ssbash << i*n+j << "\t";
            }
            ssbash << endl;
       }
       print2bash( ssfile.str(), ssbash.str() );
    }

    return 0;
}
void print2bash(string file, string data){

    ssbash.str(data);
    stringstream output;
    string line;

    output << "for((i=0;i<1;i++)) do "<< endl; // Buffer all to bash at once
    while( getline(ssbash,line) )
       output << "    echo \"" << line << "\";" << endl;

    output << "done >> " << file; // Appending 
    cout << output.str() << endl;

    ssfile.clear(); ssfile.str("");
    ssbash.clear(); ssbash.str("");

    return ;
}

a regular output of ./sstreams.o

~/work/Ising$ ./sstreams.o 4 2
for((i=0;i<1;i++)) do 
    echo "0 1   2   3   ";
    echo "4 5   6   7   ";
    echo "8 9   10  11  ";
    echo "12    13  14  15  ";
done >> chain_p=0_filename.dat

for((i=0;i<1;i++)) do 
    echo "0 1   2   3   ";
    echo "4 5   6   7   ";
    echo "8 9   10  11  ";
    echo "12    13  14  15  ";
done >> chain_p=1_filename.dat

Thanks for everything

Peter Wood
  • 23,859
  • 5
  • 60
  • 99
stringparser
  • 735
  • 1
  • 6
  • 16
  • 1
    Ok, I really don't understand why you are doing this so complicated. the calls to `clear` and `str` right after construction have no effect. – Björn Pollex Jun 04 '12 at 13:47
  • 2
    I really don't understand you overall approach. It seems like your C++ program generates a script that does almost the same thing as your C++ program does anyway. Are you sure this is the best way? – Björn Pollex Jun 04 '12 at 13:55
  • 2
    @BjörnPollex: he's a physicist, you have to understand that. – jpalecek Jun 04 '12 at 13:59
  • HaHa. I just hate open&close files, what's wrong with that? And yes, you are right. I just use `str` sometimes to fill the `ss`. Nevertheless, why I getting the error? – stringparser Jun 04 '12 at 14:01
  • 1
    @phoenix: `{ ofstream file("this_is_the_filemane"); file << 123 << "hahaha"; }` Where is the "opening & closing" that you hate? – jpalecek Jun 04 '12 at 14:04
  • "this_is_the_filename" is a char* (isn't it?) and I want it to be a stringstream so I can easyly change the name based on "the fly" considerations such as one method was used on the lattice's elements. I need that info. – stringparser Jun 04 '12 at 14:21
  • 1
    If you want to generate the name "on the fly", you can do it like this: `{ ofstream file(static_cast(ostringstream() << "on " << "the" << " fly").str()); file << 123 << "hahaha"; }` in c++11 or, if you prefer c++03, `{ ofstream file(static_cast(ostringstream() << "on " << "the" << " fly").str().c_str()); file << 123 << "hahaha"; }`. If you can construct a `string` with a filename (as you already do in your code), you can just use it as a filename for the `ofstream`. – jpalecek Jun 04 '12 at 15:46
  • I hate to say... that is kind of long. I stick to mine. Thanks anyway! – stringparser Jun 04 '12 at 16:51

2 Answers2

3

You needn't call

ssbash.clear(); ssbash.str("");

because stringstream objects are automatically created with empty contents. That leaves you with

stringstream ssbash;
stringstream ssfile;

That could be a global declaration, but don't do it, because it won't help you. You would have to clear it each time you use it.

If you don't like repeating the declaration over and over, you can substitute them with a macro

#define DECLARE_STREAMS \
  stringstream ssbash; \
  stringstream ssfile;

or better

#define DECLARE_STREAMS(a, b) \
  stringstream a; \
  stringstream b;

used like this: DECLARE_STREAMS(ssbash, ssfile).

But the best (and cleanest) solution would be to refactor code that formats the data into a single function. That way, you wouldn't have to worry about repeating the stream declaration.

BTW why do you instead of writing to a file with C++ emit script that does the writing is beyond me.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • 1
    Thanks man. lol for the last line. I'll tell you why! Let's say you want to write to 20 diferent files with 5 auxiliar files each. That is 100 files. Keeping track of them is a massive headache, cause each means something and, I **do** need them separated in the end to do the analysis. This way, with `ssfile` for the filenames and `ssbash` for the content is just a matter of doing a looping. And yes, I'm a physicist so I don't know what the heck I'm doing with C++. That's why I'm here :) – stringparser Jun 04 '12 at 14:12
  • @FredLarson: `#declare` is a preprocessor directive, which is part of C++. – jpalecek Jun 04 '12 at 15:38
  • @jpalecek: Since when? Please cite the standard. – Fred Larson Jun 04 '12 at 16:33
  • @FredLarson: Of course, I'm an idiot. It should have been `#define`, I mixed that up. – jpalecek Jun 04 '12 at 16:47
  • 1
    @jpalecek: I don't think you're an idiot. You did make a mistake, something I've never done. ;v) – Fred Larson Jun 04 '12 at 16:53
2

C/C++ forbids you to have code executed outside of functions (apart from variable declarations/definitions).

thus the following is illegal:

int main(int argc, char**argv) {
   return 0;
}
stringstream ssbash; // OK
ssbash.clear(); // ouch; procedural programming outside of context
ssbash.str(""); // ouch; procedural programming outside of context

if you need to call clear() and str(), then you must do that in an initilializing function

static stringstream ssbash, ssfile;
void initSS(void) {
   ssbash.clear(); ssbash.str("");
   ssfile.clear(); ssfile.str("");
}

int main(int argc, char**argv) {
   initSS();
   // ....
   return 0;
}
umläute
  • 28,885
  • 9
  • 68
  • 122
  • Thanks. This is clear why I couldn't. So I can't doit because of the `clear` and `str` are not ready to use. That's nice... – stringparser Jun 04 '12 at 14:16