Using Boost Python for C++ class which takes user input - c++

I have a C++ class which takes user input in the constructor and then writes that (and other stuff) onto a file. It works perfectly fine on C++ (both on MSVC and GCC) and now I would like to use this class in my Python projects. My files are:
foo.h
#include <iostream>
#include <fstream>
#include <stdio.h>
class Foo
{
public:
explicit Foo(const std::string file_name, const std::string other_input);
virtual ~Foo();
void Write(const std::string random_text);
private:
std::ofstream output_file;
char buffer[200];
std::string random_string;
};
foo.cpp
#include <Python.h>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
// Constructor
Foo::Foo(const std::string file_name, const std::string other_input)
{
std::ifstream file_exists(file_name)
if(file_exists.good())
output_file.open(file_name, std::ios_base::app);
else
output_file.open(file_name);
random_string = other_input;
}
// Destructor
Foo::~Foo()
{
output_file.close();
}
// Write to a file
void Foo::Write(const std::string random_text)
{
sprintf( buffer, "%s", random_string );
output_file << buffer << ";\n";
}
// Boost.Python wrapper
BOOST_PYTHON_MODULE(foo)
{
boost::python::class_<Foo>("Foo", boost::python::init<>())
.def("Write", &Foo::Write)
;
}
When I try to compile this on Visual Studio or GCC, I am getting the following error:
'std::basic_ofstream<_Elem,_Traits>::basic_ofstream' : cannot access private member declared in class 'std::basic_ofstream<_Elem,_Traits>'
I am totally confused regarding why this is the case. I have tried another variant of the wrapper, namely:
// Boost.Python wrapper
BOOST_PYTHON_MODULE(foo)
{
boost::python::class_<Foo, boost::noncopyable>("Foo", boost::python::init<>())
.def("Write", &Foo::Write)
;
}
And here I get the error:
'Foo' : no appropriate default constructor available
Any ideas to get this to heel would be highly appreciated!
Thanks in advance..

One apparent mistake in your code is that Foo's constructor takes two parameters that you have not included in the wrapper:
// Boost.Python wrapper
BOOST_PYTHON_MODULE(foo)
{
boost::python::class_<Foo, boost::noncopyable>("Foo",
boost::python::init<const std::string, const std::string>())
.def("Write", &Foo::Write)
;
}
This explains the second error and this version (with noncopyable) should now compile fine.

Related

File pointer does not works when moved to a class?

So I found this solution to call a python script and it works in Microsoft Visual Studio Professional 2019 Version 16.6.2. I moved the three methods into a separate class and it no longer works. I thought I could figure it out but have just been beating myself up so I am here asking for help from readers who are much more knowledgeable than myself. The working code follows.
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <iostream>
#include <string>
void close_file(std::FILE* fp) {
std::fclose(fp);
}
std::string exec_python(const char* scriptCommand) {
std::array<char, 256> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&close_file)> _pipe(_popen(scriptCommand, "r"), close_file);
if (!_pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), _pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
std::string call_script(std::string ScriptLoc, std::string Script_Parameters) {
std::string ScriptInput = ScriptLoc + " " + Script_Parameters;
std::string result = exec_python(ScriptInput.c_str());
return result;
}
int main()
{
std::string LOne = "python.exe \"C:\\Users\\.....\\source\\repos\\PyApplication1\\PyApplication1.py\"";
std::string LTwo = " Message";
//system(LTwo.c_str());
std::string oRez=call_script(LOne,LTwo);
std::cout <<"Results:"+oRez << std::endl;
}
The separate class that will not compile and related error messages follow. first is the header file, iTool.h.
#pragma once
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <iostream>
#include <string>
class iTool {
public:
void close_file(std::FILE* fp);
std::string exec_python(const char* scriptCommand);
std::string call_script(std::string ScriptLoc, std::string Script_Parameters);
iTool();
};
The second is the iTool.cpp.
#include "iTool.h"
void iTool::close_file(std::FILE* fp) {
std::fclose(fp);
}
std::string iTool::exec_python(const char* scriptCommand) {
std::array<char, 256> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&close_file)> _pipe(_popen(scriptCommand, "r"), close_file);
if (!_pipe) {
//throw std::runtime_error("_popen() failed!");
} else { throw std::runtime_error("_popen() failed!"); }
while (fgets(buffer.data(), static_cast<int>(buffer.size()), _pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
std::string iTool::call_script(std::string ScriptLoc, std::string Script_Parameters) {
std::string ScriptInput = ScriptLoc + " " + Script_Parameters;
std::string result = exec_python(ScriptInput.c_str());
return result;
}
iTool::iTool() = default;
The error messages.:
Severity Code Description Project File Line Suppression State
Error C2088 '!': illegal for class TestAppliance1 C:\Users\....\source\repos\TestAppliance1\iTool.cpp 11
Error C2276 '&': illegal operation on bound member function expression TestAppliance1 C:\Users\...\source\repos\TestAppliance1\iTool.cpp 10
Error C2514 'std::unique_ptr': class template cannot be constructed TestAppliance1 C:\Users\...\source\repos\TestAppliance1\iTool.cpp 10
Error C2660 'fgets': function does not take 2 arguments TestAppliance1 C:\Users\...\source\repos\TestAppliance1\iTool.cpp 14
Error C2662 '_Get_deleter_pointer_type<_Ty,remove_reference<_Ty1>::type,void>::type std::unique_ptr<_Ty,_Dx>::get(void) noexcept const': cannot convert 'this' pointer from 'std::unique_ptr' to 'const std::unique_ptr<_Ty,_Dx> &' TestAppliance1 C:\Users\...\source\repos\TestAppliance1\iTool.cpp 14
Thanks for your help!
You tripped over a non-static member function having a hidden this parameter and not matching the prototype expected for a deleter function.
Instead of void (*)(std::File*), the proposed deleter looks something like void (iTool::*(std::File*)
You can solve the problem by wrapping the function call with a lamda expression that captures this, but since close_file doesn't use this (something wrong with that--fp should probably be a member variable--that is outside the scope of this question), lets go with making it a static member function. The asker can clean up the ideological problems later.
In the iTool class definition,
void close_file(std::FILE* fp);
becomes
static void close_file(std::FILE* fp);
After you create a class you must create an instance of that class in the main function before you can use it:
iTool tool()
Inside you mean function.....
Also one word of caution for you my friend, if you do not know what:
iTool::iTool() = default;
Sets your variables to please do not use it...... You can always define those variables by yourself in your private part, if you know what the default does, by all means go ahead.

Making a class templatized on stream handle both std::cout and an std::ofstream

I want to write a class StreamContainer that is templatized on Stream:
#ifndef STREAMCONTAINER_HPP
#define STREAMCONTAINER_HPP
#include <string>
#include <iostream>
template<typename Stream>
class StreamContainer
{
public:
StreamContainer(std::ostream& os)
: m_stream(os) {}
private:
Stream & m_stream;
};
#endif
I would think that the following client code would work:
#include "StreamContainer.hpp"
#include <fstream>
int main(int argc, char argv[])
{
std::ofstream ofs;
ofs.open("c:\\code\\temp.txt");
StreamContainer<decltype(std::cout)> coutContainer(std::cout); // C2439
StreamContainer<std::ofstream> fileContainer(ofs); // C2664
}
But that doesn't work, at least in Visual C++ 2015. Trying to pass std::cout causes error C2439 (member could not be initialized) and trying to pass a std::ofstream object causes error C2664 (std::basic_ofstream constructor can't convert argument from std::basic_ostream to const char *). I've also tried using a move constructor, but had other issues with that. Any suggestions as to how to resolve this would be greatly appreciated.
This:
StreamContainer(std::ostream& os)
should be:
StreamContainer(Stream& os)
Otherwise, your ofstream instantiation is trying to take an ofstream& reference (m_stream) to an ostream (os). The other direction is fine, but this is assigning a base class object to a derived reference.
Since you can't deduce class template arguments from constructor arguments, this is a good use-case for just introducing a factory function:
template <typename Stream>
StreamContainer<Stream> make_container(Stream& s) {
return StreamContainer<Stream>{s};
}
so that you don't have to repeat the arguments or, worse, use decltype:
auto coutContainer = make_container(std::cout);
auto fileContainer = make_container(ofs);
Or, really, both can just be ostream& if you don't need any of the type-specifics.

regarding streams in c++

I want to use file streams and consol output stream. In constructor, I want to initialize with either file or consol output stream depending on parameter passed to constructor. Then I will have another function in class which will redirect output to that stream. what will be the code for it? I am trying with the below code which is not working.
Any other design suggestions are welcome.
class Test
{
private:
std::ios *obj;
std::ofstream file;
std::ostream cout1;
public:
// Test(){}
Test(char choice[])
{
if(choice=="file")
{
obj=new ofstream();
obj->open("temp.txt");
}
else
obj=new ostream();
}
void printarray()
{
for(int i=0;i<5;i++)
(*obj)<<"\n \n"<<"HI"
}
};
Something like this should work:
#include <iostream>
#include <fstream>
#include <string>
class Test
{
private:
std::ofstream file;
std::ostream& obj;
public:
// Use overloaded constructors. When the default constructor is used,
// use std::cout. When the constructor with string is used, use the argument
// as the file to write to.
Test() : obj(std::cout) {}
Test(std::string const& f) : file(f.c_str()), obj(file) {}
void printarray()
{
for(int i=0;i<5;i++)
obj<<"\n " << "HI" << " \n";
}
};
int main()
{
Test a;
a.printarray();
Test b("out.txt");
b.printarray();
}
PS Look at the changes to printarray. What you were trying, with %s, is good for the printf family of functions but not for std::ostream.
Any other design suggestions are welcome.
Two of these members are useless:
std::ios *obj;
std::ofstream file;
std::ostream cout1;
You can't do anything with a std::ios, a std::ostream that isn't associated with a streambuf is useless, and you never use file or cout1 anyway!
You want:
std::ofstream file;
std::ostream& out;
as shown in R Sahu's answer, and write to out.
Test(char choice[])
{
if(choice=="file")
This doesn't work, you need to use strcmp to compare char strings. You should probably use std::string not char*.

Proper way to create ostream to file or cout

I am trying to create a Log class for my project at school. It needs to either be able to write information to the stdout or to a file depending on the parameters it is passed. I was looking into how to do this and I stumbled upon a thread with a similar question here: Obtain a std::ostream either from std::cout or std::ofstream(file)
The only difference between this thread and my own is that I want to do it inside of a class. Looking at the solution though they use std::ostream out(buf) and construct the ostream on the fly with buf. How can i declare this properly in my Log class to be able to construct the "out" object only once i enter my Log constructor?
I took a quick stab at it below but I am not sure if it is correct or if I am on the right track. Appreciate any help, thanks.
EDIT: I want to be able to do out << "Some string" << endl; after i get this Log class working properly.
EDIT2: An error I am now receiving with the new code below error : 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char, _Traits = std::char_traits<char>]' is protected
// log.h
#include <string>
#include <fstream>
#ifndef LOG_H_
#define LOG_H_
class Log
{
public:
enum Mode { STDOUT, FILE };
// Needed by default
Log(const char *file = NULL);
~Log();
// Writing methods
void write(char *);
void write(std::string);
private:
Mode mode;
std::streambuf *buf;
std::ofstream of;
std::ostream out;
};
#endif
// log.cpp
#include "log.h"
#include <iostream>
#include <stdlib.h>
#include <time.h>
Log::Log(const char *file)
{
if (file != NULL)
{
of.open(file);
buf = of.rdbuf();
mode = FILE;
}
else
{
buf = std::cout.rdbuf();
mode = STDOUT;
}
// Attach to out
out.rdbuf(buf);
}
Log::~Log()
{
if (mode == FILE)
of.close();
}
void Log::write(std::string s)
{
out << s << std::endl;
}
void Log::write(char *s)
{
out << s << std::endl;
}
You create tmp with std::ostream tmp(buf); and store the address of it in out with this->out = &tmp;. However, tmp will go out of scope at the end of the constructor and the pointer will no longer be pointing at a valid object.
What you should do instead is make out not a std::ostream* but simply a std::ostream:
std::ostream out;
Then in your constructor, once you've got the buf ready, you can give it to out by doing out.rdbuf(buf);.
Response to edit:
The std::ostream doesn't have a default constructor - it has to take a buffer pointer. My mistake. However, the fix is simple. Use your constructor's member initialization list to pass a null pointer (nullptr in C++11, 0 or NULL in C++03):
Log::Log(const char *file)
: out(nullptr)
{
// ...
}

Term not evaluated to a function in C++

I have following code, which i taken from Boost and simplified for my project. Please accept my aplogies for pasting complete code, i done it so that it will be easy to answer my question. While compiling following code in VS 2008 i am getting followoing error.
error C2064: term does not evaluate to a function taking 3 arguments
I am expecting addOptions retruns OptionsInit object which call function operator with three arguments but that is not happening, can any one please find bug. Thanks in advance.
namespace MyInfrastructure
{
namespace Internal
{
class OptionDescrp;
class OptionsInit;
}
class OptionsCollection
{
public:
OptionsCollection(std::string optCollName);
Internal::OptionsInit addOptions();
private:
// avoid copying and assignment.
// Prohibit copy
OptionsCollection( const OptionsCollection& );
OptionsCollection& operator = (const OptionsCollection& );
void add(Internal::OptionDescrp* desc) {m_options.push_back(desc);}
std::vector<Internal::OptionDescrp* > m_options;
std::string m_optCollName;
friend class Internal::OptionsInit;
};
}
////////////
#include <string>
#include <vector>
#include <assert.h>
#include "PrgmOptions.h"
namespace MyInfrastructure
{
namespace Internal
{
class OptionDescrp
{
public:
OptionDescrp(std::string pcOptname, std::string description, bool isOptValueReq);
virtual ~OptionDescrp(){ };
private:
std::string m_shortName; // option short name.
std::string m_longName; // option long name.
std::string m_description;// option description.
};
class OptionsInit
{
public:
OptionsInit(OptionsCollection* coll){ owner = coll; }
OptionsInit& operator()(std::string name, std::string description, bool isOptValReq);
private:
OptionsCollection* owner;
};
}
/////
namespace MyInfrastructure
{
OptionsCollection::OptionsCollection(std::string optCollName) : m_optCollName(optCollName) {}
Internal::OptionsInit OptionsCollection::addOptions()
{
return Internal::OptionsInit(this);
}
}
namespace MyInfrastructure
{
namespace Internal
{
// Class Options description definitions.
OptionDescrp::OptionDescrp(std::string pcOptname, std::string description, bool isOptValueReq)
: m_description(description)
{
std::string name(pcOptname);
std::string::size_type n = name.find(',');
if (n != std::string::npos)
{
assert(n == name.size()-2);
m_longName = name.substr(0, n);
m_shortName = '-' + name.substr(n+1,1);
}
else
{
m_longName = name;
}
}
// Class Options Init definitions.
OptionsInit& OptionsInit::operator()(std::string name, std::string description, bool isOptValReq)
{
OptionDescrp* opt = new OptionDescrp(name, description, isOptValReq);
owner->add(opt);
return *this;
}
}
}
//////
int main(void)
{
MyInfrastructure::OptionsCollection desc("myoptions");
**desc.addOptions()("help", "produce help message", false); // error is thrown here**
return 0;
}
The example code in the question compiles without errors with Visual 2008, gcc, Visual 2003 when we copy all in a single file.
You have error C2064, it is probably because you either have a #define or another definition somewhere in other headers that you did not include in your sample, or that somehow you are not compiling exactly the sample code.
Try to copy all the sample code in a single file and compile that.
Interesting code: OptionsInit returned by addOptions() is a temporary. You are then calling a non-const method on it, which is allowed, but it returns a non-const reference to itself which is also allowed because it's a non-const method. But that means essentially you "backdoor" binding a non-const reference to a temporary...
I assume the two asterisks before desc.addOptions are not really in your code as there is no operator* overloaded here.
Perhaps if you make operator() const and return const-reference it will work.
problem is with VS2008. I compiled with VS2010, it compiled fine. Thanks all for the inputs.