Search code examples
c++singletonofstream

Singleton ofstram: Files created but did not be written


I create a singleton class to wrap some ofstream

struct OutputFiles {
 public: 
  std::ofstream minus_total, plus_total, HoughMappingMinus_cc, HoughMappingPlus_cc;

  template <typename T>
  static void init(OutputFilesName<T>& files_name) {
    instance_impl(files_name);
  }

  static OutputFiles& instance() {
    return instance_impl();
  }

  template <typename T>
  OutputFiles(OutputFilesName<T>& files_name) :
    minus_total(files_name.resut_subdir_name + "/" + files_name.result_file_name_data_M),
    plus_total(files_name.resut_subdir_name + "/" + files_name.result_file_name_data_P),
    HoughMappingMinus_cc(files_name.resut_subdir_name + "/" + files_name.result_file_name_M_CC),
    HoughMappingPlus_cc(files_name.resut_subdir_name + "/" + files_name.result_file_name_P_CC)
  { }
  OutputFiles() = default;
  OutputFiles(const OutputFiles&) = delete;
  OutputFiles& operator=(const OutputFiles&) = delete;

 private:
  template <typename... Args>
  static OutputFiles& instance_impl(Args&&... args) {
    static OutputFiles singleton( std::forward<Args&&>(args)... );
    return singleton;
  }
};

This class must be initialize with OutputFilesName<T> class

template <typename T>
struct OutputFilesName {
  OutputFilesName(
    const T&& i_resut_subdir_name,
    const T&& i_result_file_name_M_CC,
    const T&& i_result_file_name_P_CC,
    const T&& i_result_file_name_data_M,
    const T&& i_result_file_name_data_P
  ) : 
    resut_subdir_name(i_result_file_name_M_CC),
    result_file_name_M_CC(i_result_file_name_M_CC),
    result_file_name_P_CC(i_result_file_name_P_CC),
    result_file_name_data_M(i_result_file_name_data_M),
    result_file_name_data_P(i_result_file_name_data_P)
  { }
  const T resut_subdir_name;
  const T result_file_name_M_CC;
  const T result_file_name_P_CC;
  const T result_file_name_data_M;
  const T result_file_name_data_P;
};

I write main code invoke it:

int main(){
    OutputFilesName<std::string> fn{"fa_name","fb_name","aa","bb","cc"};
    fs::create_directory(fn.resut_subdir_name);
    OutputFiles::init(fn);
    OutputFiles::instance().minus_total << "1" << std::endl;
    OutputFiles::instance().plus_total << "2" << std::endl;
    OutputFiles::instance().HoughMappingMinus_cc << "3"<< std::endl;
    OutputFiles::instance().HoughMappingPlus_cc << "4"<< std::endl;
    bool open1 = OutputFiles::instance().minus_total.is_open();
    bool open2 = OutputFiles::instance().plus_total.is_open();
    bool open3 = OutputFiles::instance().HoughMappingMinus_cc.is_open();
    bool open4 = OutputFiles::instance().HoughMappingPlus_cc.is_open();
    std::cout << open1 << open2 << open3 << open4 << std::endl;
}

The result and full code here,

ofstream seems can't be open.

Files and directory has been created,

but the content is empty.

I'm confused.


Solution

  • This is a strange way to manage objects.

    Your OutputFiles::init(fn) invokes instance_impl(fn), a function template specialisation. That specialisation has a static local variable called singleton, instantiated with ctor args fn, to which you return a reference. All good.

    Then, you do OutputFiles::instance() to get a OutputFiles&. Trouble is, that function invokes instance_impl(), a different function template specialisation. That specialisation also has a static local variable called singleton, default-constructed. All of its ofstream members have also thus been default-constructed, and streaming to them accomplishes nothing.

    The key is that the two specialisations of instance_impl are different functions. So this is not a valid way to bypass static initialisation.

    Why not just instantiate an OutputFiles in the normal way and store it somewhere?