I am having an odd problem:
metadata.h:
class metadata
{
public:
metadata ( std::string filename );
template <class T> T get ( std::string key ) { return m_data.get<T> ( key ); }
private:
boost::property_tree::ptree m_data;
};
metadata.cpp:
metadata::metadata ( std::string filename )
{
try {
boost::property_tree::read_info ( filename, m_data );
} catch ( boost::property_tree::file_parser_error err ) {
std::cerr << "[!] Unable to open "
<< filename
<< ": "
<< err.message()
<< "!"
<< std::endl;
throw except ( "Error opening metadata file." );
}
}
asset.h:
template <class T> class assetManager {
public:
void load ( std::string filename );
T get ( std::string filename );
T operator[] ( std::string filename );
void drop ( std::string filename ) { m_assets.erase ( filename ); }
void clear() { m_assets.clear(); }
private:
std::map<std::string,T> m_assets;
};
template <class T> void assetManager<T>::load ( std::string filename )
{
if ( m_assets [ filename ] == nullptr ) {
m_assets.erase ( filename );
m_assets.insert ( std::pair <std::string,T> ( filename, new T ( filename ) ) );
}
}
template <class T> T assetManager<T>::get ( std::string filename )
{
T ret = m_assets.at ( filename );
return ret;
}
For some reason, this line here:
m_metadata.load ( boost::filesystem::current_path().string() + "/engine.conf" );
I am getting the following compiler error from g++:
src/core/../core/../asset/asset.h|22|error: cannot convert ‘std::string {aka std::basic_string}’ to ‘metadata*’ in initialization
At no point, as far as I can tell, have I programmed anything even slightly resembling a conversion of a string into my metdata pointer here. Any ideas?
Inside assetManager<T>::load this subexpression
std::pair <std::string,T> ( filename, new T ( filename ) )
is obviously incorrect. new T will produce T *. Meanwhile your pair is declared with T as its second member. You cannot use T * to initialize T, unless you have a proper conversion constructor in T.
If T is metadata, then for this problem I'd actually expect an error message saying that it is impossible to convert metadata * to std::string, not the other way around. You will see the same error if you attempt this
metadata m(new metadata("test"));
Another issue is that you are creating a pair in which the first member has type std::string. However, in std::map<std::string, T>::value_type the first member of the pair is actually const std::string. The code will compile, but it will require a conversion from your std::pair<std::string, T> to map's std::pair<const std::string, T>. This might become performance issue. Which is why a much better idea would be to use std::map<std::string, T>::value_type as pair type, instead of trying to spell it out manually.
In the comments you stated that m_metadata is assetManager<metadata*>. In that case T is metadata *. That explains everything, including the error message. In that case the problem is even more simple and localized. This
new T ( filename )
alone is what causing the error. That means that new T produces a metadata * object and attempts to initialize it with std::string. Hence the error message. It is not possible to initialize metadata * with std::string because it is not possible to convert std::string to metadata *.
On top of that, such new expression will return a metadata ** value, which is definitely not what you need.
I changed m_metadata to an assetManager then made a few changes thus:
template <class T> class assetManager {
public:
void load ( std::string filename );
T get ( std::string filename );
T operator[] ( std::string filename );
void drop ( std::string filename ) { m_assets.erase ( filename ); }
void clear() { m_assets.clear(); }
private:
std::map<std::string,T*> m_assets;
};
template <class T> void assetManager<T>::load ( std::string filename )
{
if ( m_assets [ filename ] == nullptr ) {
m_assets.erase ( filename );
m_assets.insert ( std::pair <std::string,T*> ( filename, new T ( filename ) ) );
}
}
template <class T> T assetManager<T>::get ( std::string filename )
{
T ret = *m_assets.at ( filename );
return ret;
}
It compiles and runs now. Though my problem is fixed it is still a mystery to me why the compiler thinks I was trying a std::string to metadata* conversion.
Related
I am trying to make a generic stringToVector function.
The input is a string that contains multiple int or string separated by comma (let's ignore char)
ex)
[1, 5, 7] or [conver, to, string, vector]
I want a generic function like this
template <class T>
vector<T> stringToVector(string input) {
vector<T> output;
input = input.substr(1, input.length() - 2);
stringstream ss;
ss.str(input);
T item;
char delim = ',';
while (getline(ss, item, delim)) {
if (is_same(T, int)) {
output.push_back(stoi(item)); // error if T is string
} else {
output.push_back(item); // error if T is int
}
}
return output;
}
Is there any way around?
I know this function is dumb, but I just want this for a competitive programming.
Usually it is done by a helper function:
template<class T>
T my_convert( std::string data );
template<>
std::string my_convert( std::string data )
{
return data;
}
template<>
int my_convert( std::string data )
{
return std::stoi( data );
}
inside your function:
str::string str;
while (getline(ss, str, delim))
output.push_back( my_convert<T>( std::move( str ) ) );
it will fail to compile for any other type than std::string or int but you can add more specializations of my_convert if you need to support other types.
I created a class called SkipToChar that's supposed to be able to be used as follows:
std::ostringstream oss;
oss << "Hello," << SkipToChar(7) << "world!" << std::endl;
Which would print "Hello, world!" (notice the space.) Basically it's supposed to skip to the character at the specified index using spaces. But apparently the compiler doesn't recognize the operator<< I created for it. Interestingly, calling operator<< explicitly, even without giving any template parameters (like operator<<(oss, SkipToChar(7)); works fine; it just doesn't work if I actual
Here's my code:
#include <iostream>
#include <sstream>
template <typename _Elem>
struct basic_SkipToChar
{
typename std::basic_string<_Elem>::size_type pos;
basic_SkipToChar(typename std::basic_string<_Elem>::size_type position)
{
pos = position;
}
};
template <typename _Elem>
inline std::basic_ostringstream<_Elem> &operator<<(std::basic_ostringstream<_Elem> &oss, const basic_SkipToChar<_Elem> &skip)
{
typename std::basic_string<_Elem>::size_type length = oss.str().length();
for (typename std::basic_string<_Elem>::size_type i = length; i < skip.pos; i++) {
oss << (_Elem)' ';
}
return oss;
}
typedef basic_SkipToChar<char> SkipToChar;
typedef basic_SkipToChar<wchar_t> WSkipToChar;
int main(int argc, char *argv[])
{
std::ostringstream oss;
/*ERROR*/ oss << "Hello," << SkipToChar(8) << "world!" << std::endl;
std::cout << oss.str();
return 0;
}
It gives me the following error when I try to compile it:
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'basic_SkipToChar<char>' (or there is no acceptable conversion)
I marked the line the error is on with a comment. What's going wrong here?
oss << "Hello," returns std::basic_ostream<char> (it loses the string part).
So your method doesn't match (expect std::basic_ostringstream but got std::basic_ostream<char>).
As Jarod42 points out, the return type of all of the built-in
operator<< is std::ostream&. This is a basic principle of
how streams operator; except for the actual sink or source, the
stream should be indifferent as to where the data is going to or
coming from.
It's possible to provide manipulators which only work for one
type of stream, or do different things for different types of
streams, by using a dynamic_cast in the manipulator:
std::ostream&
operator<<( std::ostream& dest, SkipToChar const& manip )
{
std::ostringstream* d = dynamic_cast<std::ostringstream*>( &dest );
if ( d != nullptr ) {
// ...
}
return dest;
}
In general, however, this is a very bad idea; clients will be
very surprised that outputting to a string results in different
text than outputting to a file.
What you seem to be trying to do is to more or less emulate
a form of tabbing. The best way to do this is to use
a filtering output streambuf, which keeps track of where you are
in the line, and can be inserted between the std::ostream and
the actual sink regardless of the type of stream:
class TabbingOutputStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ostream* myOwner;
int myInLineCount;
public:
TabbingOutputStreambuf( std::streambuf* dest )
: myDest( dest )
, myOwner( nullptr )
, myInLineCount( 0 )
{
}
TabbingOutputStreambuf( std::ostream& dest )
: myDest( dest.rdbuf() )
, myOwner( &dest )
, myInLineCount( 0 )
{
myOwner.rdbuf( this );
}
~TabbingOutputStreambuf()
{
if ( myOwner != nullptr ) {
myOwner->rdbuf( myDest );
}
}
int overflow( int ch ) override
{
if ( ch == '\n' ) {
myInLineCount = 0;
} else {
++ myInLineCount;
}
return myDest->sputc( ch );
}
// Special function...
int tabTo( int n )
{
int retval = 0;
while ( retval == 0 && myInLineCount < n ) {
if ( overflow( ' ' ) == EOF ) {
retval = EOF;
}
}
return retval;
}
};
Your manipulator would then be:
std::ostream&
operator<<( std::ostream& dest, SkipToChar const& manip )
{
TabbingOutputStreambuf* sb = dynamic_cast<TabbingOutputStreambuf*>( dest.rdbuf() );
assert( sb != nullptr );
try {
if ( sb->tabTo( manip.pos ) == EOF ) {
dest.setstate( std::badbit );
}
} catch ( ... ) {
dest.setstate( std::badbit );
}
return dest;
}
This is still not ideal, since it will fail with an unprotected
buffer, but you'd only use the manipulator in a context like:
void
generateOutput( std::ostream& dest )
{
TabbingOutputStreambuf tabber( dest );
dest << "Hello, " << SkipToChar( 7 ) << "world!";
// ...
}
And this will work regardless of the type of stream passed to
the function (including custom ostream classes that you don't
even know about).
EDIT:
One last point: don't bother with making things templates until you've got the basic version working. (For what it's worth, your code doesn't work correctly for wchar_t streams anyway. To output a space in them, you need to get the embedded locale, get the ctype facet from it, and use its widen member function.)
Would the answer just be C and E? I am confused as to if bool &status can be passed by reference?
You want a function that reads in data about a movie from a specified file and returns a Movie object, either via a return value or via a parameter. It should also return some indication of success or failure either via a return value or via a parameter. Which of the following would be possible prototype choices for the above requirements? Select all that apply.
A. Movie readMovie(string filename);
B. bool readMovie(string filename, Movie m);
C. Movie readMovie(string filename, bool &status);
D. bool readMovie(string filename, const Movie &m);
E. bool readMovie(string filename, Movie &m);
F. Movie readMovie(string filename, bool status);
All of them can work:
A. if Movie itself can have a well known bad value (equivalent to end, EOF or NULL), or a member to check whether it's in a good state, this is fine:
Movie readMovie(string filename);
Movie m = readMovie(filename);
if (m.good() || m != Movie::InvalidMovie || ... ) {
however, I suspect the questioner thinks this doesn't count as indicating success via a return value ...
B. if Movie is typedef'd to a pointer, or a shared_ptr, or if Movie is a lightweight wrapper for a refcounted MovieImpl object (for example), this is fine (because the caller's shared state is mutated via readMovie's copy of the wrapper)
bool readMovie(string filename, Movie m);
however, I suspect the questioner thinks this doesn't count as indicating success via a ... parameter. (NB. A's Movie could also be a typedef, smart pointer or other handle-body style thing).
C. this is definitely fine (and even the questioner knows it)
Movie readMovie(string filename, bool &status);
D. same constraints as B
bool readMovie(string filename, const Movie &m);
E. fine
bool readMovie(string filename, Movie &m);
F. stupid: this can work in the same was as A, but the status param is useless and misleading. So, I'm certain the questioner doesn't expect this to work:
Movie readMovie(string filename, bool status);
So, what can really be made to work: all of them.
What the questioner probably wants to hear: C, E.
What you should be looking at:
designing clear, expressive, efficient interfaces that are easy to use correctly (none of the above fit this, IMO)
appropriate use of exceptions instead of return codes (if failure is genuinely exceptional)
passing that string filename by const reference
getting a better teacher.
Let's see:
A provides no boolean indication of success. False.
B is passing Movie by value, so there's no way it will get returned. False.
C returns a movie, and the bool is passed by ref so it will get modified. True.
(skip)
E is the opposite of C, so it will work too. True.
(back to) D is the same as E, except it will be a const Movie this may or may not be appropriate. True?
F is the opposite of B, and fails for the same reason. False.
It's not a terribly clear question, and I would indicate my reasoning for my answers on the test/homework.
A movie is a big thing, so it should be allocated dynamically.
Therefore, use a smart pointer.
The code below demonstrates/proves how each and every proposed function signature supports returning a loaded movie and checking whether it succeeded or failed, with the most natural interpretation of the information provided (in particular, that a "movie" is a movie).
Note that the signatures in the question are all examples of Very Bad Design™.
The signature shown in namespace right in the code below is however OK:
It supports Windows filenames in general.
It ensures that if the load fails, the calling code has no movie object to (erroneously) play with, and this is done by throwing an exception to indicate failure.
It passes the string by reference to const (for efficiency and as a general good habit).
#include <memory> // std::shared_ptr
#include <new> // std::nothrow
#include <stdexcept> // std::runtime_error
#include <string> // std::string
class MovieData
{
// Whatever
};
// *** A movie is a big thing, so it should be allocated dynamically. ***
// *** Therefore, use a smart pointer. ***
//
typedef std::shared_ptr< MovieData > Movie;
namespace a {
using std::string;
using std::nothrow;
Movie readMovie(string filename)
{
return 0?0
: filename == "succeed"? Movie( ::new(nothrow) MovieData )
: Movie();
}
bool test( string const& filepath )
{
Movie const m = readMovie( filepath );
return (m != nullptr);
}
}
namespace b {
using std::string;
using std::nothrow;
bool readMovie(string filename, Movie m)
{
if( filename != "succeed" || m == nullptr )
{
return false;
}
*m = MovieData();
return true;
}
bool test( string const& filepath )
{
Movie const m( ::new(nothrow) MovieData );
return readMovie( filepath, m );
}
}
namespace c {
using std::string;
using std::nothrow;
Movie readMovie(string filename, bool &status)
{
status = false;
if( filename != "succeed" )
{
return Movie();
}
Movie const result( ::new(nothrow) MovieData );
status = true;
return result;
}
bool test( string const& filepath )
{
bool result = false;
readMovie( filepath, result );
return result;
}
}
namespace d {
using std::string;
using std::nothrow;
bool readMovie(string filename, const Movie &m)
{
if( filename != "succeed" || m == nullptr )
{
return false;
}
*m = MovieData();
return true;
}
bool test( string const& filepath )
{
Movie const m( ::new(nothrow) MovieData );
return readMovie( filepath, m );
}
}
namespace e {
using std::string;
using std::nothrow;
bool readMovie(string filename, Movie &m)
{
if( filename != "succeed" )
{
return false;
}
m.reset( ::new(nothrow) MovieData );
return (m != nullptr);
}
bool test( string const& filepath )
{
Movie m;
return readMovie( filepath, m );
}
}
namespace f {
using std::string;
using std::nothrow;
Movie readMovie(string filename, bool status)
{
(void) status; struct status; // Intentionally not used.
if( filename != "succeed" )
{
return Movie();
}
return Movie( ::new(nothrow) MovieData );
}
bool test( string const& filepath )
{
return (readMovie( filepath, bool() ) != nullptr);
}
}
namespace right {
using std::wstring; // Using wide string supports nearly all Windows filenames.
using std::runtime_error;
Movie readMovie( wstring const& filepath )
{
if( filepath != L"succeed" )
{
throw std::runtime_error( "right::readMovie: failed because ..." );
}
return Movie( new MovieData );
}
bool test( wstring const& filepath )
{
try
{
readMovie( filepath );
return true;
}
catch( ... )
{
return false;
}
}
}
#define TEST( name, filename ) \
wcout << #name "::readMovie" << "( " << #filename << " ) " \
<< (name ::test( filename )? "succeeded" : "failed") << "." << endl;
#include <iostream> // std::wcout, std::endl
int main()
{
using std::wcout; using std::endl; using std::boolalpha;
wcout << boolalpha;
TEST( a, "succeed" );
TEST( a, "fail" );
wcout << endl;
TEST( b, "succeed" );
TEST( b, "fail" );
wcout << endl;
TEST( c, "succeed" );
TEST( c, "fail" );
wcout << endl;
TEST( d, "succeed" );
TEST( d, "fail" );
wcout << endl;
TEST( e, "succeed" );
TEST( e, "fail" );
wcout << endl;
TEST( f, "succeed" );
TEST( f, "fail" );
wcout << endl;
TEST( right, L"succeed" );
TEST( right, L"fail" );
}
Output (test results):
a::readMovie( "succeed" ) succeeded.
a::readMovie( "fail" ) failed.
b::readMovie( "succeed" ) succeeded.
b::readMovie( "fail" ) failed.
c::readMovie( "succeed" ) succeeded.
c::readMovie( "fail" ) failed.
d::readMovie( "succeed" ) succeeded.
d::readMovie( "fail" ) failed.
e::readMovie( "succeed" ) succeeded.
e::readMovie( "fail" ) failed.
f::readMovie( "succeed" ) succeeded.
f::readMovie( "fail" ) failed.
right::readMovie( L"succeed" ) succeeded.
right::readMovie( L"fail" ) failed.
struct T
{
void eat(std::string const& segment)
{
buffer << segment;
std::string sentence;
while (std::getline(buffer, sentence))
std::cout << "[" << sentence.size() << "]";
}
std::stringstream buffer;
};
int main() {
T t;
t.eat("A\r\nB\nC\nD");
// ^^ ^ ^ ^
}
// Actual output: [2][1][1][1]
// Desired output: [1][1][1][1]
I would like the std::stringstream to strip that carriage return for me (and would prefer not to have to copy and modify segment).
How might I go about this? I would have thought that this would happen anyway, on Linux, for a stream in text mode... but perhaps that mechanism is in the logic of file streams.
This is a general problem on Unix machines when reading files created on
a Windows machine. I would suggest doing the clean-up at the input
level.
One of the best solution I've found when reading line based files is to
create a class something like:
class Line
{
std::string myText;
public:
friend std::istream& operator>>( std::istream& source, Line& dest )
{
std::getline( source, dest.myText );
if ( source ) {
dest.myText.erase(
std::remove( dest.myText.begin(), dest.myText.end(), '\015' ),
dest.myText.end() );
}
return source;
}
operator std::string() const
{
return myText;
}
};
You can add other functions as necessary: the automatic type conversion
doesn't play when trying to match templates, for example, and I found it
useful to add friends to wrap boost::regex_match.
I use this (without the '\015' removal) even when I don't have to
worry about Windows/Linux differences; it supports reading lines using
std::istream_iterator<Line>, for example.
Another solution would be to use a filtering streambuf, inserted into
the input stream. This is also very simple:
class RemoveCRStreambuf : public std::streambuf
{
std::streambuf* mySource;
char myBuffer; // One char buffer required for input.
protected:
int underflow()
{
int results = mySource->sbumpc();
while ( results == '\015' ) {
results = mySource->sbumpc();
}
if ( results != EOF ) {
myBuffer = results;
setg( &myBuffer, &myBuffer + 1, &myBuffer + 1 );
}
return results;
}
public:
RemoveCRStreambuf( std::streambuf* source )
: mySource( source )
{
}
};
To insert it:
std::streambuf* originalSB = source->rdbuf();
RemoveCRStreambuf newSB( originalSB );
source->rdbuf( &newSB );
// Do input here...
source->rdbuf( originalSB ); // Restore...
(Obviously, using some sort of RAII for the restoration would be
preferable. My own filtering streambuf have a constructor which takes
an std::istream; they save a pointer to this as well, and restore the
streambuf in their destructor.)
I would like to write a function which takes an fstream, processes it, and fills information in a struct I supply in the second argument.
My problem is that I am confused how to use pointers and fstreams as I get debug errors:
Access violation writing location
0xcccccccc.
Here is the main function:
int main()
{
keyframe_struct kfstruct;
string ifile = "filename";
ifstream fin( ifile, ios::binary );
load_from_keyframe_file( fin, kfstruct );
fin.close();
cout << kfstruct.num_keyframes << endl;
return 0;
}
And here is the function I try to use for parsing the binary file and filling in the information in the struct kfstruct:
struct keyframe_struct
{
int num_views;
int num_keyframes;
vector<keyframe> keyframes;
};
int load_from_keyframe_file( ifstream &fin, keyframe_struct &kfstruct )
{
char keyword[100];
while ( !fin.eof() )
{
fin.getline( keyword, 100, 0 );
if ( strcmp( keyword, "views" ) == 0 )
{
fin.read(( char* ) kfstruct.num_views, sizeof( int ) );
}
else if ( strcmp( keyword, "keyframes" ) == 0 )
{
fin.read(( char* ) kfstruct.num_keyframes, sizeof( int ) );
}
}
}
Can you tell me what am I doing wrong? I'm sure I am making some huge errors here as I am just a beginner and I still don't understand clearly what should I and what should I not do with pointers.
You forgot to take the address of your fields:
fin.read(( char* ) &kfstruct.num_views, sizeof( int ) );
^^^
[As an aside, note that it's better from a maintenance point of view to do sizeof(kfstruct.num_views). So if the type ever changes, your code will still work.]
Instead of
( char* ) kfstruct.num_views
use
( char* ) (&kfstruct.num_views)
similarly in the other place.
otherwise you are writing to a location whose address is equal to the VALUE of your int. You don't want that. You want the address converted to char*. You take the address by '&' operator.