3
\$\begingroup\$

I am making session store which will store some data and read some, This session store can store multiple data( SessionInfo) to the map.

Data Structure

#include <map>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <iostream>

template <typename T>
class IBasicStream;

namespace
{
constexpr int kClassVersion = 1;
}

/*
ToSerial and FromSerial are for backward support
*/

struct Calendar
{

  int day;
  int month;
  int year;

  Calendar() : day(0), month(0), year(0)
  {
  }

  Calendar &operator=(const Calendar &other)
  {
    day = other.day;
    month = other.month;
    year = other.year;
    return *this;
  }

  template <typename streamType>
  void ToSerial(IBasicStream<streamType> &stream)
  {
    stream << kClassVersion;
    stream << day;
    stream << month;
    stream << year;
  }

  //class version Going to use for future change;
  template <typename streamType>
  void FromSerial(IBasicStream<streamType> &stream)
  {
    int classVersion = 0;
    stream >> classVersion;
    stream >> day;
    stream >> month;
    stream >> year;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator<<(IBasicStream<streamType> &stream, Calendar &Calendar)
  {
    Calendar.ToSerial(stream);
    return stream;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator>>(IBasicStream<streamType> &stream, Calendar &Calendar)
  {
    Calendar.FromSerial(stream);
    return stream;
  }
};

struct Timings
{

  Calendar startDate;
  int numberOfDays;
  int duration;

  std::vector<Calendar> customDate;

  Timings() : startDate{}, numberOfDays(0), duration(0)
  {
  }

  Timings &operator=(const Timings &other)
  {
    startDate = other.startDate;
    numberOfDays = other.numberOfDays;
    duration = other.duration ;
    customDate = other.customDate;
    return *this;
  }

  template <typename streamType>
  void ToSerial(IBasicStream<streamType> &stream)
  {
    stream << kClassVersion;
    stream << startDate;
    stream << numberOfDays;
    stream << duration;
    int size = int(customDate.size());
    stream << size;
    for (auto item : customDate)
    {
      stream << item;
    }
  }

  template <typename streamType>
  void FromSerial(IBasicStream<streamType> &stream)
  {
    int size = -1;
    int classVersion = 0;
    stream >> classVersion;
    stream >> startDate;
    stream >> numberOfDays;
    stream >> duration;

    stream >> size;
    customDate.clear();
    for (int i = 0; i < size; i++)
    {
      Calendar item;
      stream >> item;
      customDate.push_back(item);
    }
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator<<(IBasicStream<streamType> &stream, Timings &timings)
  {
    timings.ToSerial(stream);
    return stream;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator>>(IBasicStream<streamType> &stream, Timings &timings)
  {
    timings.FromSerial(stream);
    return stream;
  }
};

// It should have title
// Reminder Title
// TODO: priority

struct Description
{

  std::string title;
  std::string reminderTitle;

  Description() : title(""), reminderTitle("")
  {
  }

  Description &operator=(const Description &other)
  {
    title = other.title;
    reminderTitle = other.reminderTitle;
    return *this;
  }

  template <typename streamType>
  void ToSerial(IBasicStream<streamType> &stream)
  {
    stream << kClassVersion;
    stream << title;
    stream << reminderTitle;
  }

  template <typename streamType>
  void FromSerial(IBasicStream<streamType> &stream)
  {
    int classVersion = 0;
    stream >> classVersion;
    stream >> title;
    stream >> reminderTitle;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator<<(IBasicStream<streamType> &stream, Description &description)
  {
    description.ToSerial(stream);
    return stream;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator>>(IBasicStream<streamType> &stream, Description &description)
  {
    description.FromSerial(stream);
    return stream;
  }
};

enum State
{
  Started,
  Ended,
  Suspended,
  InProgress,
  Invalid,
};

class SessionInfo
{

public:
  //ToDo : Way to make ctor private as only
  //so that Session Class can only access
  //but std::map is currently accessing It so not
  //possible write now.

  SessionInfo() noexcept : mDescription(), mTimings(), mState(State::Started)
  {
  }

  SessionInfo(const SessionInfo &other) : mDescription(other.mDescription),
                                          mTimings(other.mTimings),
                                          mState(other.mState)
  {
  }

  SessionInfo &operator=(const SessionInfo &other)
  {
    mDescription = other.mDescription;
    mTimings = other.mTimings;
    mState = other.mState;
    return *this;
  }

  SessionInfo(SessionInfo &&other) : mDescription(std::move(other.mDescription)),
                                     mTimings(std::move(other.mTimings)),
                                     mState(std::move(other.mState))
  {
  }

  SessionInfo &operator=(SessionInfo &&other)
  {
    mDescription = std::move(other.mDescription);
    mTimings = std::move(other.mTimings);
    mState = std::move(other.mState);
    return *this;
  }

  SessionInfo &SetTitle(const std::string &title)
  {
    mDescription.title = title;
    return *this;
  }

  ~SessionInfo()
  {
  }

  std::string GetTitle() const
  {
    return mDescription.title;
  }

  SessionInfo &SetState(const State &other)
  {
    mState = other;
    return *this;
  }

  State GetState()
  {
    return mState;
  }

  SessionInfo &SetReminderTitle(const std::string &reminderTitle)
  {
    mDescription.reminderTitle = reminderTitle;
    return *this;
  }

  std::string GetReminderTitle() const
  {
    return mDescription.reminderTitle;
  }

  SessionInfo &SetDescription(const Description &other)
  {
    mDescription.reminderTitle = other.reminderTitle;
    mDescription.title = other.title;
    return *this;
  }

  Description &GetDescription()
  {
    return mDescription;
  }

  template <typename streamType>
  void ToSerial(IBasicStream<streamType> &stream)
  {
    stream << kClassVersion;
    stream << mDescription;
    stream << mTimings;
    int state = mState;
    stream << state;
  }

  template <typename streamType>
  void FromSerial(IBasicStream<streamType> &stream)
  {
    stream >> kClassVersion;
    stream >> mDescription;
    stream >> mTimings;
    int state = 0;
    stream >> state;
    mState = (State)state;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator>>(IBasicStream<streamType> &stream, SessionInfo &obj)
  {
    obj.FromSerial(stream);
    return stream;
  }

  template <typename streamType>
  friend IBasicStream<streamType> &operator<<(IBasicStream<streamType> &stream, SessionInfo &obj)
  {
    obj.ToSerial(stream);
    return stream;
  }

  SessionInfo &SetTimings(const Timings &other)
  {
    mTimings = other;
    return *this;
  }

private:
  //template<typename T> friend class ISessionDataBaseFactory;
  //friend class              Session;
  Description mDescription;
  Timings mTimings;
  State mState;
};

This is the class which is responsible for storing session information with its interface.

template <typename childClass>

class ISessionDataBaseFactory
{
public:
  ISessionDataBaseFactory() noexcept
  {
  }

  template <typename streamType>

  bool LoadSession(IBasicStream<streamType> &stream)
  {
    auto childObj = static_cast<childClass *>(this);
    return childObj->LoadSessionImpl(stream);
  }

  template <typename streamType>
  void SaveAllSession(IBasicStream<streamType> &stream)
  {
    auto childObj = static_cast<childClass *>(this);
    childObj->SaveAllSessionImpl(stream);
  }

  void SetSessionInfo(std::string &sessionId, SessionInfo &info)
  {
    auto childObj = static_cast<childClass *>(this);
    childObj->SetSessionInfoImpl(sessionId, info);
  }

  SessionInfo &GetSessionInfo(const std::string &sessionId)
  {
    auto childObj = static_cast<childClass *>(this);
    return childObj->GetSessionInfoImpl(sessionId);
  }
  /* will be making SessionInfo as private */
  SessionInfo &BuildSessionInfo()
  {
    auto childObj = static_cast<childClass *>(this);
    return childObj->BuildSessionInfoImpl();
  }
};

//

class Session : public ISessionDataBaseFactory<Session>
{

public:
  Session() noexcept : mSessionStore{}, invalidStateObj{}, session{}
  {
    invalidStateObj.SetState(State::Invalid);
    session.SetState(State::Started);
    mSessionStore.clear();
  }

  /*
  The members of the map are internally stored in a tree structure. 
  There is no way to build the tree until you know the keys and values 
  that are to be stored.
  */

  template <typename streamType>
  bool LoadSessionImpl(IBasicStream<streamType> &stream)
  {
    int size = -1;
    stream >> size;

    if (stream.IsValid() && size > 0)
    {
      for (int i = 0; i < size; i++)
      {
        std::string key;
        SessionInfo value;
        stream >> key;
        stream >> value;
        mSessionStore.insert(std::make_pair(key, value));
      }

      return true;
    }
    return false;
  }

  //TODO : in future invalid mState object is going to use more

  SessionInfo &GetSessionInfoImpl(const std::string &sessionId)
  {
    int size = mSessionStore.size();

    if (size > 0 && mSessionStore.find(sessionId) != mSessionStore.end())
    {
      return mSessionStore[sessionId];
    }
    else
    {
      return invalidStateObj;
    }
  }

  template <typename streamType>
  void SaveAllSessionImpl(IBasicStream<streamType> &stream)
  {

    if (mSessionStore.size() > 0)
    {
      int size = int(mSessionStore.size());
      stream << size;
      for (auto &item : mSessionStore)
      {
        stream << item.first;
        stream << item.second;
      }
      stream.Flush();
    }
  }

  //TODO: Add logic to genrate session ID's
  void SetSessionInfoImpl(std::string &sessionId, SessionInfo &info)
  {
    if (mSessionStore.size() > 0 && mSessionStore.find(sessionId) != mSessionStore.end())
    {
      mSessionStore[sessionId] = info;
    }
    else
    {
      mSessionStore.insert(std::make_pair(sessionId, info));
    }
  }

  SessionInfo &BuildSessionInfoImpl()
  {
    return session;
  }

  ~Session() noexcept
  {
  }

  //Timing Methods

private:
  std::map<std::string, SessionInfo> mSessionStore;
  SessionInfo invalidStateObj;
  SessionInfo session;
};

/*

*/

These classes responsible for proving stream classes.

template <typename childClass>
class IBasicStream
{
public:
  IBasicStream() noexcept
  {
  }

  IBasicStream(const std::string filename) noexcept
  {
  }

  IBasicStream(IBasicStream<childClass> &&other)
  {
    //Nothing needed ...
  }

  ~IBasicStream()
  {
  }

  IBasicStream<childClass> &operator=(IBasicStream<childClass> &&other)
  {
    // Nothing n
  }

  void Open(const std::string &filePath)
  {
    auto child = static_cast<childClass *>(this);
    child->OpenImpl(filePath);
  }

  bool IsValid()
  {
    auto child = static_cast<childClass *>(this);
    return child->IsValidImpl();
  }

  /***Issue here is that 
      i need operator ">>" and "<<" for all
      the implicit types but i don't want 
      for class so can't make genric.
      may be need to create for double and other class Too

      other are using friend function to do that 
    ***/
  void operator<<(int data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  void operator<<(char data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  /* void operator<<(std::string & data) {
    auto child = static_cast< childClass * >(this);
    child->FromSerial(data);
  }*/

  void operator<<(float data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  void operator<<(std::basic_string<char> data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  void operator>>(std::basic_string<char> data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  void operator>>(char data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  /*void operator>>(std::string & data) {
    auto child = static_cast< childClass * >(this);
    child->FromSerial(data);
  }*/

  void operator>>(float data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  void operator>>(int data)
  {
    auto child = static_cast<childClass *>(this);
    child->FromSerial(data);
  }

  bool IsEmpty()
  {
    auto child = static_cast<childClass *>(this);
    return child->IsEmptyImpl();
  }

  void Clear()
  {
    auto child = static_cast<childClass *>(this);
    child->ClearImpl();
  }

  void Flush()
  {
    auto child = static_cast<childClass *>(this);
    child->FlushImpl();
  }
};

class BinaryStream : public IBasicStream<BinaryStream>
{
public:
  BinaryStream() : Parent()
  {
  }

  BinaryStream(const std::string filename) noexcept
  {
    mBinaryStream.open(filename, std::fstream::in | std::fstream::out | std::fstream::trunc);
  }

  void OpenImpl(const std::string filename)
  {
    if (!IsValidImpl())
    {
      mBinaryStream.open(filename, std::fstream::in | std::fstream::out | std::fstream::trunc);
    }
  }

  BinaryStream(BinaryStream &&other) : Parent(std::move(other))
  {
    *this = std::move(other);
  }

  BinaryStream &operator=(BinaryStream &&other)
  {
    mBinaryStream = std::move(other.mBinaryStream);
    return *this;
  }

  template <typename T>
  void ToSerial(T &data)
  {
    if (mBinaryStream.is_open())
    {
      mBinaryStream << data;
    }
  }

  template <typename T>
  void FromSerial(T &data)
  {
    if (mBinaryStream.is_open())
    {
      mBinaryStream >> data;
    }
  }

  bool IsValidImpl()
  {
    return mBinaryStream.is_open();
  }

  bool IsEmptyImpl()
  {
    return mBinaryStream.peek() == std::fstream::traits_type::eof();
  }

  void ClearImpl()
  {
    if (mBinaryStream.is_open())
    {
      mBinaryStream.clear();
    }
  }

  void FlushImpl()
  {
    if (mBinaryStream.is_open())
    {
      mBinaryStream.flush();
      mBinaryStream.sync();
    }
  }

  friend std::fstream &operator<<(std::fstream &stream, std::string &str)
  {
    int length = 0;
    length = str.size();
    stream << length;
    stream.write(str.c_str(), length);
    return stream;
  }

  friend std::fstream &operator>>(std::fstream &stream, std::string &str)
  {
    int length = 0;
    stream >> length;
    char cstr[length];
    stream.read(cstr, length);
    str = std::string(cstr);
    return stream;
  }

private:
  typedef IBasicStream<BinaryStream> Parent;
  std::fstream mBinaryStream;
};

// Save Session Class

Test case :

int main() 
{

  std::shared_ptr<IBasicStream<BinaryStream>> mystream(new BinaryStream("ABC"));
  std::cout << "mystream is valid: " << mystream->IsValid() <<std::endl;
  std::shared_ptr<ISessionDataBaseFactory< Session >> sess(new Session());
  auto sessinfo(sess->BuildSessionInfo());
  sessinfo.SetState(State::Invalid);
  sessinfo.SetReminderTitle("This is awful");
  std::string as ="Anb";

  sess->SetSessionInfo(as,sessinfo);
  sess->SaveAllSession(*mystream);
  //bool status = sess->LoadSession(*mystream);
  //TODO Flushing Issue needs to resolve
  std::cout << "mystream is valid: " << mystream->IsValid() << std::endl;
  auto sesinfo_other = sess->GetSessionInfo(as);

  if(sesinfo.GetReminderTitle() == sessinfo_other.GetReminderTitle()) 
  {
    std::cout <<"Both sessions are equal as expected " << std::endl;
  }
  else 
  {
    std::cout <<"TODO Flushing Issue needs to resolve " << std::endl;
  }
  return 0; 
}
\$\endgroup\$
7
  • 2
    \$\begingroup\$ You can improve the question by adding main and some test cases that will demonstrate how the classes are used. \$\endgroup\$
    – pacmaninbw
    Commented Sep 2, 2019 at 18:02
  • 1
    \$\begingroup\$ Sure, Thanks doing it right now :) . \$\endgroup\$
    – Mohit
    Commented Sep 2, 2019 at 18:31
  • \$\begingroup\$ What is <io.h>? \$\endgroup\$
    – L. F.
    Commented Sep 3, 2019 at 10:19
  • \$\begingroup\$ No use for that now I should be removing \$\endgroup\$
    – Mohit
    Commented Sep 3, 2019 at 12:48
  • 1
    \$\begingroup\$ #include <memory> is missing, std::end is unknown, at least to my compiler and me. You may also receive a lot improvement suggestions by compiling with warnings enabled ;-) \$\endgroup\$
    – Cornholio
    Commented Sep 3, 2019 at 18:55

1 Answer 1

2
\$\begingroup\$

I accidentally deleted my first review, so this will only cover the main points:


  • bug: I think this is the main reason your code doesn't work. Flushing isn't the issue.

    void operator<<(int data)
    {
        auto child = static_cast<childClass *>(this);
        child->FromSerial(data);
    }
    
    void operator>>(int data)
    {
        auto child = static_cast<childClass *>(this);
        child->FromSerial(data);
    }
    
  • bug: std::fstream::in | std::fstream::out | std::fstream::trunc. This truncates the input file, so we won't be able to read its content.

  • bug: Conforming C++ compilers do not allow variable length arrays. You will need to use a std::string or std::vector instead.

    int length = 0;
    stream >> length;
    char cstr[length];
    
  • operator<< and operator>> do formatted input and output (i.e. they read and write text), so BinaryStream is a very misleading name.

  • bug: they also don't insert whitespace when writing (so your file will look like: 1Anb11This is awful110000004. However, they do depend on whitespace when reading. So your current input system simply won't work.

  • Note that for text input, many reliable XML and JSON libraries already exist.

  • int does not cover the correct range of values for storing an index into, or the size of, a standard container. Use std::size_t instead.


C++ uses run-time polymorphism (inheritance and virtual functions) so that classes of different types can implement the same interface, and be referred to through a common base class:

class ISession
{
public:

    virtual void Load(Stream& stream) = 0; // abstract fucntion
    virtual void Save(Stream& stream) const = 0;
};

class SessionA : public ISession
{
    virtual void Load(Stream& stream) override
    {
        // ... implementation specific to SessionA
    }
    virtual void Save(Stream& stream) const override
    {
        // ... implementation specific to SessionA
    }
};

class SessionB : public ISession
{
    virtual void Load(Stream& stream) override
    {
        // ... implementation specific to SessionB
    }
    virtual void Save(Stream& stream) const override
    {
        // ... implementation specific to SessionB
    }
};

std::vector<std::unique_ptr<ISession>> sessions;
// ...

for (auto const& s : sessions)
    s->Save(stream);

However, your current implementation does not allow this, since (e.g.) ISessionBaseFactory<SessionA> and ISessionBaseFactory<SessionB> are not a common base class, but entirely different types.

If we need only static (compile-time) polymorphism, we can use templates instead, and avoid the common base class entirely.

class SessionA
{
    void Load(Stream& stream);
    void Save(Stream& stream) const;
};

class SessionB
{
    void Load(Stream& stream);
    void Save(Stream& stream) const;
};

template<class SessionT>
void DoSomething(SessionT const& session)
{
    Stream stream;
    session.Save(stream); // works with SessionA and SessionB
}

It appears, however, that you don't actually need either kind of polymorphism, since there is only one session type.

\$\endgroup\$
2
  • \$\begingroup\$ 1) I am intentionally using Compile time polymorphism as it can be faster and we can achieve almost same behavior(no static used). 2) Thanks for pointing out stream bug nd int/size bug i am working on that i will update the code. \$\endgroup\$
    – Mohit
    Commented Sep 5, 2019 at 11:22
  • \$\begingroup\$ Great. (Also, just to note that when you update the code it's best to create a new question instead of editing the existing one - that way the answers still make sense). \$\endgroup\$
    – user673679
    Commented Sep 5, 2019 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.