0

Consider the following code snippet of this class template...

template<class T>
class FileTemplate {
private:
    std::vector<T>   vals_;
    std::string  filenameAndPath_;

public:
    inline FileTemplate( const std::string& filenameAndPath, const T& multiplier ) :
    filenameAndPath_( filenameAndPath ) {

        std::fstream file;
        if ( !filenameAndPath_.empty() ) {
            file.open( filenameAndPath_ );
            T val = 0;

            while ( file >> val ) {
                vals_.push_back( val );
            }

            file.close();

            for ( unsigned i = 0; i < vals_.size(); i++ ) {
                vals_[i] *= multiplier;
            }

            file.open( filenameAndPath_ );

            for ( unsigned i = 0; i < vals_.size(); i++ ) {
                file << vals_[i] << " ";
            }

            file.close();

        }
    }

    inline std::vector<T> getValues() const {
        return vals_;
    }

};

When used in main as such with the lower section commented out with the following pre-populated text file:

values.txt

1 2 3 4 5 6 7 8 9

int main() {

    std::string filenameAndPath( "_build/values.txt" );
    std::fstream file;

    FileTemplate<unsigned> ft( filenameAndPath, 5 );

    std::vector<unsigned> results = ft.getValues();

    for ( auto r : results ) {
        std::cout << r << " ";
    }
    std::cout << std::endl;

    /*
    FileTemplate<float> ft2( filenameAndPath, 2.5f );
    std::vector<float> results2 = ft2.getValues();

    for ( auto r : results2 ) {
        std::cout << r << " ";
    }
    std::cout << std::endl;
    */

    std::cout << "\nPress any key and enter to quit." << std::endl;
    char q;
    std::cin >> q;
    return 0;
}

and I run this code through the debugger sure enough both the output to the screen and file are changed to

values.txt - overwritten are -

5 10 15 20 25 30 35 40 45 

then lets say I don't change any code just stop the debugging or running of the application, and let's say I run this again 2 more times, the outputs respectively are:

values.txt - iterations 2 & 3

25 50 75 100 125 150 175 200 225 250

125 250 375 500 625 750 875 1000 1125 1250

Okay good so far; now lets reset our values in the text file back to default and lets uncomment the 2nd instantiation of this class template for the float with a multiplier value of 2.5f and then run this 3 times.

values.txt - reset to default

 1 2 3 4 5 6 7 8 9

-iterations 1,2 & 3 with both unsigned & float the multipliers are <5,2.5> respectively. 5 for the unsigned and 2.5 for the float


- Iteration 1

cout:

 5 10 15 20 25 30 35 40 45
 12.5 25 37.5 50 62.5 75 87.5 100 112.5

values.txt:

 12.5 25 37.5 50 62.5 75 87.5 100 112.5


- Iteration 2

cout:

 60
 150 12.5 62.5 93.75 125 156.25 187.5 218.75 250 281.25

values.txt:

 150 12.5 62.5 93.75 125 156.25 187.5 218.75 250 281.25


- Iteration 3

cout:

 750 60
 1875 150 12.5 156.25 234.375 312.5 390.625 468.75 546.875 625 703.125

values.txt:

 1875 150 12.5 156.25 234.375 312.5 390.625 468.75 546.875 625 703.125

A couple of questions come to mind: it is two fold regarding the same behavior of this program.

The first and primary question is: Are the file read and write calls being done at compile time considering this is a class template and the constructor is inline?

After running the debugger a couple of times; why is the output incrementing the number of values in the file? I started off with 9, but after an iteration or so there are 10, then 11.


This part just for fun if you want to answer:

The third and final question yes is opinion based but merely for educational purposes for I would like to see what the community thinks about this: What are the pros & cons to this type of programming? What are the potentials and the limits? Are their any practical real world applications & production benefits to this?

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • 4
    _Are the file read and write calls being done at compile time_ no. –  Jan 04 '18 at 14:49
  • @manni66 Oh okay I was wondering if they were or not because most templates are... Yet I kind of figured they weren't because I don't see any way of making an fstream object a `constexpr` – Francis Cugler Jan 04 '18 at 14:50
  • 1
    No, they are not. –  Jan 04 '18 at 14:51
  • 1
    @FrancisCugler You seem to be confused about what a template is. When a template is instantiated, it defines a new function or class. But that function or class is no different than anything you would have written yourself, meaning a templated function it's no more run at compile-time than a non-templated function. Perhaps you are thinking of [template metaprogramming](https://stackoverflow.com/questions/514644/what-exactly-is-metaprogramming). – François Andrieux Jan 04 '18 at 14:53
  • @FrançoisAndrieux Not exactly I do know what templates are, but I kind of was thinking about template metaprogramming. If it is possible to use the fstream objects in meta programming would it still hold the same? – Francis Cugler Jan 04 '18 at 14:59
  • 1
    @FrancisCugler The only portable mechanism in c++ that can access files at compile time is `#include`. – François Andrieux Jan 04 '18 at 15:04
  • @FrançoisAndrieux That does make sense about the portability since we do have different architectures, operating systems, compilers, hardware etc. – Francis Cugler Jan 04 '18 at 15:05
  • It could be argued that even #include doesn't access any files at "compile-time", since it's processed by the preprocessor which is executed *before* the compiler. – Sean Burton Jan 04 '18 at 16:14

1 Answers1

1

In terms of the other issues. The main issue is that you are not truncating the file when you do the second file.open statement, you need :

file.open( filenameAndPath_, std::fstream::trunc|std::fstream::out );

What is happening, is that, when you are reading unsigned int from a file containing floating points, it is only reading the first number (e.g. 12.5) up to the decimal place and then stopping (e.g. reading only 12) , because there is no other text on the line that looks like an unsigned int. This means it only reads the number 12 and then multiplies it by 5 to get the 60, and writes it to the file.

Unfortunately because you don't truncate the file when writing the 60, it leaves the original text at the end which is interpreted as additional numbers in the next read loop. Hence, 12.5 appears in the file as 60 5

stream buffers Extracts as many characters as possible from the stream and inserts them into the output sequence controlled by the stream buffer object pointed by sb (if any), until either the input sequence is exhausted or the function fails to insert into the object pointed by sb.

(http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/)

the4thamigo_uk
  • 835
  • 4
  • 8
  • Okay that makes sense and the output is as it should be for both the "int" & "float" types. However I was liking the fact that it was reading from and writing to the file with the existing data after each iteration of running the app. – Francis Cugler Jan 04 '18 at 21:21