ArkM 1,090 Postaholic

Exactly!

ArkM 1,090 Postaholic

I hope you know that it's impossible in general case. For example, an user might type 1st countings of his Geiger sensor;). It's a series too...

ArkM 1,090 Postaholic

Let's remember character literals in C and C++: sizeof('C') == sizeof(int) in C but it's not true in C++ (because of 'C' has int type in C but char type in C++).
See also for more subtle distinctions:
http://www.trilithium.com/johan/2005/01/char-types/

ArkM 1,090 Postaholic

Also pay attention: getchar(), getc(), fgetc() functions return int (not char) values.
In essence, char type in C (but not in C++) is a very short int, that's all.

ArkM 1,090 Postaholic

To Salem:
Formally you are right, but...
To be honest we must write tons of words, for example:

// ... don't forget to check conversion result...
// ... better write a function ...
bool getInt(int& target, const char* source)
{
    bool ok = false;
    if (source)
    {
        std::istringstream istr(source);
        if (istr >> target)
            ok = true;
    }
    return ok;
}
// ... or use strtol() ...
char stopchar;
final = static_const<int>(std::strtol(value_a,&stopchar,10));
if (std::errno == ERANGE)
   ...
// ... or use C++ exception to signal overflow or bad data ...
   ...

And so on...
What's an awful answer to the simplest (in addition incorrectly formulated;)) question!
It seems the only result of that explanation: the burnt child dreads the fire (or C++;))...

ArkM 1,090 Postaholic

I think, at this level of the programming skill atoi() is a suitable solution. For example, stringsteam solution (see above) does not check the result too.
Maniacally check this, check that == not to see the wood for the trees ;)...

ArkM 1,090 Postaholic

Or simply use old good atoi() from <cstdlib>:

char* value_a = "123456";
int final = std::atoi(value_a);
ArkM 1,090 Postaholic

Yet another tip.
There is a wonderful class valarray in C++ STL. It lives in (undeserved) obscurity.
If you declare your array as a valarray, for example:

std::valarray<int> a(10);

and fill it from the console (as an usual array), you may use its member functions, for example:

a.sum()             // to get a sum of all element values
a.sum()/a.size() // to get an average of...

No loops, no troubles...

ArkM 1,090 Postaholic

Let a is an array of comparable items.
Let's define functions inv(i,j), inv(i):

{ 1 if a[k] < a[m], k > m
inv{k,m) = |  1 if a[i] > a[m], k < m
                 { 0 otherwise
            
inv(k)   = sum of inv(k,i), i != k

Obviously, sum of inv(i), i = [0..n) (let it named as an array defect, or inv(a) ) is equal to 0 if and only if an array is ordered. In this terms we can reformulate the task specification now:

Find the shortest element by element removing sequence a => a' => ... =>a* where inv(a*) == 0.

If we remove k-th element from the array (transform a => a')

inv(a') = inv(a) - 2*inv(k)

So the step of (optimal) algorithm is:

select k: inv(k) == max of inv(i), i = [0..n')
 remove a'[k] (a' => a")
 recalculate inv(i) for all elements of a"

Taking into account that inv'(k') <= inv(k) for every leaved array element, we may conclude:
1. It's true optimal algorithm.
2. There are several possible solutions in common case.

ArkM 1,090 Postaholic

It's a slow O(n*n*n) with not optimal implementation solution.
It works (I have a proof of the algorithm correctness):

int setRank(const int* a, int n, int* b)
{
    int tot = 0;
    for (int v, s, k = 0; k < n; ++k)
    {
        if (b[k] >= 0)
        {
            s = 0;
            v = a[k];
            for (int i = 0; i < k; ++i)
                if (b[i] >= 0 && a[i] > v)
                    ++s;
            for (int i = k + 1; i < n; ++i)
                if (b[i] >= 0 && a[i] < v)
                    ++s;
            tot += (b[k] = s);
        }
    }
    return tot;
}

int idxMax(const int* b, int n)
{
    if (!b || n <= 1)
        return 0;
    int idx;
    for (idx = 0; idx < n && b[idx] < 0; ++idx)
        ;
    if (idx >= n)
        return -1;
    int val = b[idx];
    for (int i = idx + 1; i < n; ++i)
        if (b[i] >= 0 && b[i] > val)
            val = b[idx=i];
    return idx;
}

int makeOrder(const int* a, int n)
{
    if (!a || n <= 1)
        return 0;
    int *b = new int[n];
    for (int i = 0; i < n; ++i)
        b[i] = 0;

    int idx;
    while (setRank(a,n,b))
    {
        idx = idxMax(b,n);
        b[idx] = -1;
    }

    int skp = 0;
    for (int i = 0; i < n; ++i)
        if (b[i] < 0)
        {
            std::cout << '[' << i << "]:(" << a[i] << ")\n";
            ++skp;
        }
    std::cout << "\nRemoved:" << skp …
ArkM 1,090 Postaholic

1. Use CODE tags for your code snippet:

[code=cplusplus] your code here

[/code]
2. Try to correct absolutely barbaric text indentation.

3. Invalid file opened check in the loop, must be:

if (!fin /*!file_name*/ ) { 
        cerr << "Error: file #" << m << " could not be opened" << endl;
        exit(1);
    }

No need in fin.close() at the end of a loop body. You declare fin in the body block so fin is a local for loop body variable, it created and destroyed (closed) on every loop circuit.

Try to correct this source and at first check if your files are opened.

ArkM 1,090 Postaholic

Use resize(n), not reserve(n) vector member function.
The last one does not initialize vector elements, it only reserved memory for future vector grows.

ArkM 1,090 Postaholic

Of course, bigInt constructors must be public. But true reason of your troubles is incorrect RSA constructor. That's a right form:

RSA::RSA(unsigned m):n(m)
{};

And no need in default constructor...

More serious remark: presented RSA constructor does not initialize its bigInt member at all!
See what happens:

RSA::RSA(unsigned m)
{
   bigInt n(m);
}

Remember: a constructor is a (special, of course, but only) function. This function creates LOCAL variable n (it's not a member n!) then discard it when finished. So RSA member n wants a default constructor (w/o arguments). But no implicit default constructor if a class has user-defined constructor...

That's all, folks!..

Oh, yet another remark. RSA::RSA(unsigned) defines implicit conversion from unsigned to RSA. Obviously, it's unwanted side effect in this case. Don't forget to add a magic word explicit in this constructor declaration:

class RSA
{
   explicit RSA(unsigned);
   bigInt n;
};

No this useless (may be dangerous) convertion now...

Good luck!

ArkM 1,090 Postaholic

As usually, a proper typedef helps to clear codes:

class Movie
{
    typedef void (Movie::*MovieFunc)();
public:
    Movie()
    {
        f.push_back(&Movie::heHe);
        f.push_back(&Movie::haHa);
    }
    void show(int part) { (this->*f[part])(); }
private:
    void heHe() { cout << "He he" << endl; }
    void haHa() { cout << "Ha ha" << endl; }

    vector<MovieFunc> f;
};

void Serial()
{
    Movie m;
    m.show(0);
    m.show(1);
}
Alex Edwards commented: I agree, that's much more readable! +3
ArkM 1,090 Postaholic

2 gregorynoob:
Yes, you are on the right track; there are no two ways about it ;).

ArkM 1,090 Postaholic

In general, answer negative.

A mapping of zero..seven bit fileld to a byte (to bits of num member in your case) is implementation-defined. Of course, it's possible to make experiments, but one morning (evening, night), when you install the great new C++ compiler...

ArkM 1,090 Postaholic

It's not only a matter of personal preferences.

I have seen codes where using namespaces std provokes names conflict errors storm. Have you noticed that a code with explicit namespace prefixes is not depended of that "personal preferences" of its (future, unknown) users? It's especially concern of this (and similar) forum snippets (particularly in answers). Explicit std:: prefix used in your header files (in template definitions especially) guarantee an independence of includes and using directives arrangement in cpp files.

The more reusing potential of the code, the more an importance of the full name qualifications without using namespace directives at all (not only for std namespace).

In many cases using-declaration (not using-directive) is enough. For example:

using std::cout;
using std::ostream;
using std::ios;
using std::string;

Consider a typical case in a serious (not homework;)) project. You need (must?) use third-party libraries. Do you want to open all its namespaces in any point your own code? Do you want to rename your own conflicted names after that or to get a very strange results when foreign entities were linked to your code in unexpected manner? But namespace std is the great universe with thousands of names...

So using namespace std is not a good or a bad directive. It is. Don't fall into extremities. It's not a matter of a tastes, it's a matter of reasonable choice.

See also:
http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.5
http://www.codepedia.com/1/CppNamespace

ArkM 1,090 Postaholic

Some useful links (brief and clear texts) about unions:
http://www.cenapad.unicamp.br/parque/manuais/Ibmcxx/language/ref/rucldun.htm
and bit-fields:
http://www.cenapad.unicamp.br/parque/manuais/Ibmcxx/language/ref/rucldst.htm#decusebit

Some quotes on this topic:

The assembly generated for the bit-field structure is undoubtedly the largest and
slowest. These are the types of constructs that make C/C++ infamous for code-bloat. It is
twice as large and more than twice as slow as its peers. From inspection of the generated
assembly, it is clear that the C/C++ compiler generates a load/modify/store sequence for
every access of a bit group in the structure (Listing 24). It would be hard to be more
inefficient.

http://www.open-std.org/jtc1/sc22/wg21/docs/ESC_Boston_01_304_paper.pdf

A note of caution: Portability
Bit fields are a convenient way to express many difficult operations. However, bit fields
do suffer from a lack of portability between platforms:

integers may be signed or unsigned
Many compilers limit the maximum number of bits in the bit field to the size of an
integer which may be either 16-bit or 32-bit varieties.
Some bit field members are stored left to right others are stored right to left in
memory.
If bit fields too large, next bit field may be stored consecutively in memory
(overlapping the boundary between memory locations) or in the next word of memory.

http://www.cs.cf.ac.uk/Dave/C/node13.html

A compiler may pack consecutive bit-fields of a class, struct, or union into a single
addressable storage unit. However, the order of allocation of those bit-fields …

ArkM 1,090 Postaholic

1. May be so...

2. Alas, I have used bit fields over 30 years ago in C...

Of course, it was my opinion. Of course, tastes differ...

ArkM 1,090 Postaholic

I have posted the answer yesterday but DaniWeb site discard it (why?).

You can't redefine >> operator for bit fields because of no such animal as a reference to bit field in C++ language.

Use intermediate int variable then assign it to the bit field - but it's not >> operator, of course.

In general bit fields are useless part of C (and C++) language (next to nothing).
Better get it out of your head and go on...

ArkM 1,090 Postaholic

Some corrections and improvements.

1. Never read file data in tea-spoon by tea-spoon manner. Reasonable minimal portion is (obviously) disk sector size (not less than 512 bytes). Don't use streamsize as a name of your variables. It's not an error but streamsize is a name of STL stream size type. As usually, an optimal strategy for data reading is "the more at once the better".

2. Don't pick with cumbersome and unnecessary buffer filling. Look at the code sceleton:

bool isAscii = true;
    ifstream inFile("readme.txt",ios::binary);
    // That's right play of streamsize:
    const streamsize gPortion = 512;
    streamsize gCount;
    char* buffer = new char[gPortion];
    
    while (!inFile.eof())
    {
        inFile.read(buffer,gPortion);
        // Get the last op byte counter:
        gCount = inFile.gcount();
        if (gCount <= 0)
            break;
         // process gCount bytes
        int i;
        for (i = 0; i < gCount && isascii(buffer[i]); ++i)
           ;
        if (i < gCount)
        {
            isAscii = false;
            break;
        }
    }
    // Report result in isAscii var here...
    delete [] buffer;

3. Get the filename from cmd parameter of ask it from the console:

int main(int argc, char* argv[])
{
    const char* fname;
    string sname;
    ...
    if (argc > 1)
       fname = argv[1];
    else
    {
        cout << "Enter filename...";
        getline(cin,sname);
        fname = sname.c_str();
    }
    ifstream infile(fname,ios::binary);
    ...
ArkM 1,090 Postaholic

Obviously, it was an accidental Ancient Dragon's inattention. Of course, you have to manually use the first argument if you want to know the reason of that sensation;).

ArkM 1,090 Postaholic

1. If you want to get/check every byte in the file, open it in binary mode then use read() member function:

inFile.open("list.txt",ios::binary);
...
if (!infile.read(buffer,length))
{
    cerr << "Can\'t read a file" << endl;
    return 2;
}

2. Right condition after loop:

if (i >= lentgh) // not if (type == 1)

3. It's not so good text file identification method. For example, a file with all zero bytes is not a text file (obviously) but your program classifies it as a text file. Think about more reliable method...

ArkM 1,090 Postaholic
#include <ctype.h>
// or #include <cctype> in C++
if (isascii(c))
{...

It's the same as (c&~0x7F)==0...

krebstar commented: Thanks :) +1
ArkM 1,090 Postaholic

It's not interesting code fragments.

Although class FILENAME is a clumsy and useless "wrapper" for file name string (full uncontrolled access to one and only one only private variable;)), it works as expected. So your problem, obviously, does not bear a relation to string assignment.

Let me ask again: where is the last (before fated using) assignment to it iterator? Have you it != files.end() test? Are you sure that files list is not empty?

For example, you can't dereference it == files.end(), you can't ++it if it == files.end() - and so on. I think, bad it value is the only reason of all these troubles.

Add tracing code, check iterator position as you wish...

ArkM 1,090 Postaholic

Where is it var declaration and initialization code?
This pointer variable must refer to FILENAME object. Obviously, it points to nowhere...

ArkM 1,090 Postaholic

Aha! MinGW compiler reports error message...

Try this: add typename keyword before i var declaration in for loop

template <class T>
class CSplit
{
    typedef std::list<std::vector<T> > ListVector;
private:
    ListVector/*std::list<std::vector<T> >*/ m_Events;
public:
    bool Event (int Channel, int Message);
};

template <class T>
bool CSplit<T>::Event(int Channel, int Message)
{
    //...
    for (typename ListVector::iterator i = m_Events.begin();
        i != m_Events.end();
        i++)
    { 
        //... 
    }

That's OK for MinGW (and for VC++ 9 too).
See C++ typename in template definition for more details...

ArkM 1,090 Postaholic

Nothing criminal with VC++ 2008.
Are you sure that the snippet corresponds to the real context?
What a compiler are you using?

Some tip-comforter - use typedefs in templates:

template <class T>
class CSplit
{
    typedef std::list<std::vector<T> > ListVector;
private:
    ListVector m_Events;
public:
    bool Event (int Channel, int Message);
};

template <class T>
bool CSplit<T>::Event(int Channel, int Message)
{
    //...
    for (ListVector::iterator i = m_Events.begin();
    //...

See STL headers for examples.

ArkM 1,090 Postaholic

1. Where is a code snippet with this diagnostic message?

2. Presented source is obviously incorrect but it can't print a number. Why strcat? You print name and last_name but one line before strcat appends last_name to name...

Never use cin >> char_array ! Type a long (>19 chars) name and your program overwrite its memory. Use

#include <string>
#include <iostream>
using namespace std;
...
string name;
...
getline(name,cin);
...

if you accept names with blanks (why not? Eric Maria and so on).

ArkM 1,090 Postaholic

Use magical word virtual in

class rotatableList {
...
virtual void rotate () ...
...
};

and try again...

ArkM 1,090 Postaholic

In modern C++ operator || returns bool, not integer!

ArkM 1,090 Postaholic

Don't worry: bool, true and false IDENTIFIERS - names of program entities (types, variables, functions etc). Now they called keywords or reserved words in C++.

It's my attempt to warn NickyU against hasty decorations of C++ sources...

ArkM 1,090 Postaholic

During "pre-bool period" of C++ language (x || y) was an expression of type int (not bool), as in C language. Its possible values were 0 or 1 (by definition).

Probably, in (x || y ) == true expression the venerable elder (BC++ 3.1) made an attempt to convert int value of (x||y) to "enum bool" (named "bool" in C++) to compare with "true" "enum bool" constant. In modern C++ it's impossible. Many years ago it was only doubtful operation in C++ - so the compiler confined himself to a short warning message.

Moral#1: Avoid obviously bad style a la "boolean_expression == true (or false)" (follow CoolGamer48's advice).

Moral#2: Never, never use bool, true and false identifiers in your C++ programs, even if you have a very old compiler...

ArkM 1,090 Postaholic

Moreover, visually identical functions with int and short int arguments have different compiled bodies codes and calling codes too. For example, to pass short int arg the compiler emits "push half-word" CPU instruction but for int arg it emits "load word" instruction...

ArkM 1,090 Postaholic

Well, it's so easy... You know a number of fields (rflds). Just before while loop write:

vector<int> maxlen(rflds,0);
int fldlen;

Now insert obvious statements in the inner loop:

while (...)
{
    ...
    for (int i = 0; ...
    {
         stu = ...
         fldlen = stu.Length();
         if (fldlen > maxlen[i])
            maxlen[i] = fldlen;
         ...
     }
}
// All done: dataset and maxlen vectors are ready to use...

You have maxlen vector where maxlen is equal to max length of the i-th field.

Honestly, I can't understand why it's so hard subtask for you. It's much more easy thing than the other ADO stuff in your program.

Apropos, why print loop header is so complicated? Why FieldNum < Students->FieldCount ? You know that it's rflds (see your source next lines above). Why StudentNum's 1st value is 1? Why the 1st dataset vector element dataset[0] is an empty one and real records are placed from the 2nd row?

It seems it's inaccurate, incostistent and error prone approach...

ArkM 1,090 Postaholic

1. Honestly speaking, I don't understand what exactly you want to find: maximal length of the data in every column, or the max length of the field in the dataset, or a number of the record with longest data fileld (or what else).
2. God knows, how many String classes are in C++ world now (but there is one and only one std::string class, thank God!).

May be, you can post me/us what's your String class length member function (or property or what else;))?

ArkM 1,090 Postaholic

What's a problem? You initialize inner vectors incorrectly. Furthemore, you must take a decision for a suitable vector elements indexation (StudentNum starts from 1, but the 1st index in a vector is equal to 0).

int StudentNum = 1, rflds;
TDataSet *Students = DSet;
String stu;
rflds = Students->FieldCount;  //Number of fields in the dataset
vector< vector<String> > dataset(Students->RecordCount, vector<String>());
while (StudentNum <= Students->RecordCount)
{
    Students->RecNo = StudentNum;
    for (int i = 0; i < rflds; i++)
    {
        stu = Students->Fields->Fields[i]->AsString;
        dataset[StudentNum-1].push_back(stu);
    }
    StudentNum++;
}

Now you have an initialized vector of vectors of String. Process it as you wish (in 2D array manner, for example);

for (size_t i = 0; i < dataset.size; ++i)
{
     for (size_t j = 0; dataset[i].size(); ++i)
     {
      ..... dataset[i][j] ...
     }
}
ArkM 1,090 Postaholic

Sorry, must be va_start(p,what); , of course...

ArkM 1,090 Postaholic

More precisely: va_args does not work correctly if an argument class objects has non-trivial assignment operator (std::string, for example). You may pass pointers to any class objects safety.
However, look at the trick:

void battleShipIowa(const char* what,...)
{
    va_list p;
    va_start(p,hdr);
    std::string who;

    who = *(std::string*)p; // very dangerous, but...
    if (what)
       cout << what << "...";
    cout <<  who << "!.." << endl;

    va_end(p);
}

void TestArgs()
{
    std::string kamikaze("Kamikaze");
    battleShip("Air-raid alert!",kamikaze);
}

It's not recommendation, of course...

ArkM 1,090 Postaholic

See "The Design and Evolution of C++" by B.Stroustrup, 3.7 (exactly your situation as an example of initial references incorrectness). Obviously, Turbo C++ implements the initial version of C++ references (without later restriction not to bind non-const refs to const objects).
It's not a safety toy...
Well, so many good compilers have its own glucs...

ArkM 1,090 Postaholic

From C++ Standard:

15.5.2 The std::unexpected() function [except.unexpected]
1 If a function with an exception-specification throws an exception that is not listed in the exception-specification, the function std::unexpected() is called (18.6.2) immediately after completing the stack unwinding for the former function
2 The std::unexpected() function shall not return, but it can throw (or re-throw) an exception. If it throws a new exception which is allowed by the exception specification which previously was violated, then the search for another handler will continue at the call of the function whose exception specification was violated. If it throws or rethrows an exception that the exception-specification does not allow then the following happens: If the exception-specification does not include the class std::bad_exception (18.6.2.1) then the function std::terminate() is called, otherwise the thrown exception is replaced by an implementation-defined object of the type std::bad_exception and the search for another handler will continue at the call of the function whose exception-specification was violated.
3 Thus, an exception-specification guarantees that only the listed exceptions will be thrown.
...

and another quote:

18.6.2.2 Type unexpected_handler [lib.unexpected.handler]
typedef void (* unexpected_handler )();
1 The type of a handler function to be called by unexpected() when a function attempts to throw an exception not listed in its exception-specification.
2 Required behavior: An unexpected_handler shall not return. See also 15.5.2.
3 Default behavior: The implementation’s default unexpected_handler calls terminate().

May be, it helps.

ArkM 1,090 Postaholic

Some linguistic studies in Serialization ;):

From http://www.yourdictionary.com/serialize:

serialize Definition: to put or publish (a story , etc.) in serial form

From http://www.merriam-webster.com/dictionary/serialize:

serialize: to arrange or publish in serial form <serialize a novel>

serial: ... 6: relating to or being a connection in a computer system in which the bits of a byte are transmitted sequentially over a single wire...

From http://en.wikipedia.org/wiki/Serialization:

In computer science, in the context of data storage and transmission, serialization is the process of saving an object onto a storage medium (such as a file, or a memory buffer) or to transmit it across a network connection link in binary form. When the resulting series of bytes is reread according to the serialization format, it can be used to create an accurate clone of the original object.

Don't give heed to "in binary form" from the prev quote, add unspoken "or textual".

Now let's remember that the process of a complex data object transforming (obviously) runs step by step, member by member, recursively downto the simplest types. Of course, the ready to use term from the data transmission area (and its derivatives) was in accordance with this OOP-related stuff...

A good term without side effects - compare with so common "sin(angle")"...

ArkM 1,090 Postaholic

The most intriguing question on this forum: how pupils did their homeworks in pre-Internet era?
In the "1st method" you had incorrect, erratical behaviour: you get strlen(s) where s is not initialized (then overwritten with your program char by char input). Better forget this nightmare and follow Ancient Dragon's advice(s)...

ArkM 1,090 Postaholic

1. Revise your program logic. You have inner for loop which repeats 100 times without condition. After that you input a new command then repeat this senseless loop again...
2. There is a wonderful thing in C++. It called a function. Define a function to get empleado value from the console, don't copy these lines here, there and everywhere. May be you will get much more clear code...
3. Try to press Ctrl-Z or type not-a-digit after prompt (common misprinting, eh?). See, what heppens. No conversion error checking in your code. It's so pity...
4. Do you think that it's a pleasure to look at this wrapped (tabbed) code in the browser window? See VS Tools|Options|Text Editor|Plain Text|Tabs then switch to Insert Spaces mode...

ArkM 1,090 Postaholic

Don't worry, it's not an error: it's a VC++ warning. Ignore it (in this case). See notes in MSDN help page on this function (click strncpy then press F1).

ArkM 1,090 Postaholic

You have a class Tools with these (and others) member functions. Remember a syntax of member function call:

object.member_function_name(parameters)
// or
pointer_to_object -> member_function_name(parameters)

So, for your test (member!) function call you must write, for example:

#include "Tools.h"
...
int main()
{
    Tools tools;
    ...
    tools.test();
    ...
}

Without object specification you try to call free functions (not member functions). Of course, no such functions in your code. Apropos, it's rather suspicious code part:

test();
if (test())
...

Did you really want to call test() twice? Why?

Think again about classes, objects and member functions in C++...

ArkM 1,090 Postaholic

1. Follow Salem's advice (better use p1 = (w1 * 100) / g1 ).
2. Change the last single % in the header string literal to double %% (see printf format string specifications).
3. Better use tabs \t to adjust columns in all trailing printfs.

ArkM 1,090 Postaholic

I'm sorry, read stdafx.h, not <stdafx.h> in my prev post.

ArkM 1,090 Postaholic

Apropos, move #include <iostream> to <stdafx.h> from the main file. That's just what for stdafx.h exists. Place all <...> includes in stdafx.h. Place your own headers ("...") in your source file (or in stdafx.h too).
Read VS help about pre-compiled headers in VC++.

ArkM 1,090 Postaholic

Never write objects with std::string members (see furnished) in address-sizeof style. It's wrong to write (and read) individual std::string type members with this C-like mechanics. The size in bytes of std::string ( sizeof(std::string) is well-defined and CONSTANT value. It does not include true size of contained text. The pointer to std::string object does not refer to the contained text. You may write std::string object in a such manner but never get it back (w/o possible program crash).

So if you want to compute relative file offset for i-th record, use only basic types members with fixed length in your record structure type.