dcsimg
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 9 of 9

Thread: bizare compiler error assigning a value to a string (expected constructor, etc)

Hybrid View

  1. #1
    Join Date
    May 2009
    Location
    Boston
    Posts
    364

    bizare compiler error assigning a value to a string (expected constructor, etc)

    Hello,

    I am writing a little program and have run into a strange error doing something I must have done thousands of times. I am writing a static map<unsigned int, string> in a header file and find I can't assign a value to the string.

    Code:
    // write_file_comments.h
    
    // include guard
    #ifndef WRITE_FILE_COMMENTS_CPP_H
    #define WRITE_FILE_COMMENTS_CPP_H
    
    #include <iostream>
    #include <string>
    #include <map>
    
       // map to hold comment lines
       std::map<unsigned int, std::string> file_comments;
    
       // string to hold the map element value
       std::string element_value;
    
       // assign element
       element_value = "LL and MM";
       // also doesn't work
       // element_value.assign("LL and MM");
    
       // add to map
       file_comments['0'] = element_value;
    
    // end include guard
    #endif
    I am getting the following compiler errors at the "element_value =" line
    Code:
     error: expected constructor, destructor, or type conversion before '=' token
     error: expected `,' or `;' before '=' token
    I get the same error if I use assign() instead of the = operator.

    It is hard to imagine what I am doing with something this simple. This is c++98, but should work in any c++ that has ever been released. Am I missing some include or other? I'm sure it's something simple and dumb but I can't see it. I have code examples of other maps I have set up just like this and I have never seen this error before.

    Help would be appreciated as always,

    LMHmedchem

  2. #2
    Join Date
    Feb 2017
    Posts
    508

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    There are restrictions as to what kinds of code you can put at different places.

    If you put the assignments inside a function the code should compile. This function then obviously must be called from somewhere to take effect.
    Last edited by wolle; December 15th, 2019 at 02:16 AM.

  3. #3
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    6,977

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    At what point in your code are you inserting this header file? It looks like this code is being inserted at global scope - rather than within a function? Assignments like this can be done only within a function - not at global scope level (outside of a function).
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++17 Compiler: Microsoft VS2019 (16.4.5)

  4. #4
    Join Date
    May 2009
    Location
    Boston
    Posts
    364

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    Thanks, yes that was the problem. I made a loading function that declares the map, assigns the key/value pairs, and returns the map to the calling function. I have done this before, so I don't know why I didn't remember the correct procedure. I assume that even if you declare a global container, you still need to populate it in {} somewhere.

    What I did looks like this,
    Code:
    // file comments.h
    
    #ifndef WRITE_FILE_COMMENTS_CPP_H
    #define WRITE_FILE_COMMENTS_CPP_H
    
    #include <string>
    #include <map>
    
    // function to load and return map
    extern std::map<std::string, std::string> get_comment_lookup_map();
    
    #endif
    Code:
    // file comments.cpp
    
    #include <string>
    #include <map>
    
    using namespace std;
    
    map<string, string> get_comment_lookup_map() {
    
       // map to return
       map<string, string> comments;
    
       // assign values to map
       comments["first_key"] = "first comment";
       comments["second_key"] = "second comment";
       comments["third_key"] = "third comment";
    
    return comments;
    }
    Code:
    // file main.cpp
    
    #include <string>
    #include <map>
    #include "comments.h"
    
    using namespace std;
    
    // main function
    int main(int argc, char * argv[]) {
    
       // load map with comments
       map<string, string> comment_lookup_map = get_comment_lookup_map();
    
    return 0;
    }
    This works fine. Though it probably doesn't matter with a simple program like this, I think that formally the map should be both static and const. This is to say that once declared and populated, the map will never be altered. I am not sure how to go about implementing that. I seem to have trouble populating a map that I have declared as const.

    LMHmedchem

  5. #5
    Join Date
    Feb 2017
    Posts
    508

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    Quote Originally Posted by LMHmedchem View Post
    I am not sure how to go about implementing that. I seem to have trouble populating a map that I have declared as const.
    I do this a lot. I post a section from my production code as an example. It's a conversion function called toElement that turns atom names in the form of strings into internal counterparts.

    Since a while back my whole applications consist of .h files only (the only exception being the program entry point which is a .cpp.)


    Code:
    #ifndef CIF_UTILS_ONCE
    #define CIF_UTILS_ONCE
    
    #include "modeltypes.h"
    
    #include <cctype>
    #include <string>
    #include <unordered_map>
    
    
    namespace mcif {
    
    	inline mmodel::Element toElement(const std::string& s) {
    		static const std::unordered_map<std::string, mmodel::Element> map = {
    			{"H", mmodel::Element::H},
    			{"C", mmodel::Element::C},
    			{"N", mmodel::Element::N},
    			{"O", mmodel::Element::O},
    			{"P", mmodel::Element::P},
    			{"S", mmodel::Element::S}
    		};
    		const auto it = map.find(s);
    		return (it != map.end()) ? it->second : mmodel::Element::NONE;
    	}
    
    
          // more conversion functions .........
    }
    
    #endif
    Last edited by wolle; December 16th, 2019 at 02:14 PM.

  6. #6
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    6,977

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    You can't populate a map if it's declared const after its definition - that's what const means! You can't declare a map, then populate it, then say it's then const.

    static at global scope means that the variable can only be used in the same .cpp file.

    Are you still using C++98? If yes, be aware of the performance issues returning a container from a function - as this will probably be by value (ie copied).

    Also, in C++11 and above you can define a map using an initializer list so that it can be const.

    Assuming you're not doing multi-threading, for performance reasons you can do 'tricks' like this:

    Code:
    map<string, string>& get_comment_lookup_map() {
    
       // map to return
       static map<string, string> comments;
    
        if (comments.empty()) {
           // assign values to map
           comments["first_key"] = "first comment";
           comments["second_key"] = "second comment";
           comments["third_key"] = "third comment";
        }
    
        return comments;
    }
    as static variable within a function means that the contents of that variable is kept between different function calls - so it is only initialized once. Also, as comments is static it can be returned by ref (no copying!) as comments is now not destroyed when its scope ends as it's static. So you can do something like:

    Code:
    // load map with comments
       const map<string, string>& comment_lookup_map = get_comment_lookup_map();
    ie make comment_look_map a const ref

    How many elements is comments going to have?

    Consider:

    Code:
    #include <map>
    #include <string>
    #include <iostream>
    using namespace std;
    
    map<string, string>& get_comment_lookup_map() {
    
    	// map to return
    	static map<string, string> comments;
    
    	if (comments.empty()) {
    		// assign values to map
    		cout << "map initialization\n";
    
    		comments["first_key"] = "first comment";
    		comments["second_key"] = "second comment";
    		comments["third_key"] = "third comment";
    	}
    
    	return comments;
    }
    
    int main()
    {
    	// load map with comments
    	const map<string, string>& comment_lookup_map = get_comment_lookup_map();
    
    	for (const auto& [key, val] : comment_lookup_map)
    		cout << key << "\t" << val << endl;
    
    	const map<string, string>& comment_lookup_map1 = get_comment_lookup_map();
    
    	for (const auto& [key, val] : comment_lookup_map1)
    		cout << key << "\t" << val << endl;
    
    }
    displays:

    Code:
    map initialization
    first_key       first comment
    second_key      second comment
    third_key       third comment
    first_key       first comment
    second_key      second comment
    third_key       third comment
    with the map initialization only done once.

    Note that the code to display the map is C++17 - use here for testing for convenience!

    PS If you have at least C++11 (or is that 17 - I forget what came in what version ), then you can do:

    Code:
    const map<string, string>& get_comment_lookup_map() {
    
    	// map to return
    	const static map<string, string> comments {{"first _key", "first comment"}, {"second_key", "second comment"}, {"third_key", "third comment"}};
    
    	return comments;
    }
    Last edited by 2kaud; December 16th, 2019 at 02:02 PM. Reason: PS
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++17 Compiler: Microsoft VS2019 (16.4.5)

  7. #7
    Join Date
    May 2009
    Location
    Boston
    Posts
    364

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    I made your suggested modifications and it compiles. your changes are in red text.

    Code:
    // file comments.h
    
    #ifndef WRITE_FILE_COMMENTS_CPP_H
    #define WRITE_FILE_COMMENTS_CPP_H
    
    #include <string>
    #include <map>
    
    // function to load and return map
    extern std::map<std::string, std::string>& get_comment_lookup_map();
    
    #endif
    Code:
    // file comments.cpp
    
    #include <string>
    #include <map>
    
    using namespace std;
    
    map<string, string>& get_comment_lookup_map() {
    
       // map to return
       static map<string, string> comments;
    
       if( comments.empty() ) {
    
          // assign values to map
          comments["first_key"] = "first comment";
          comments["second_key"] = "second comment";
          comments["third_key"] = "third comment";
    
       }
    
    return comments;
    }
    Code:
    // file main.cpp
    
    #include <string>
    #include <map>
    #include "comments.h"
    
    using namespace std;
    
    // main function
    int main(int argc, char * argv[]) {
    
       // load map with comments
       const map<string, string>& comment_lookup_map = get_comment_lookup_map();
    
    return 0;
    }
    This compiles and executes without error, but the map search doesn't return anything. The search is done as,
    Code:
    // see if there is a comment saved at the key "trimmed_line"
    map<string, string>::const_iterator map_loc = comment_lookup_map.find(trimmed_line);
    where "trimmed_line" is a string with a value that might be a key in the map, and might not. I had to switch this to a const iterator to get it to compile. Find doesn't ever locate any keys in the map. I have printed the map size from the calling function and it outputs 130, which is correct. I assume this means there is some problem with my iterator. I don't know how to print map elements without an iterator, so I don't know how to test this.

    Quote Originally Posted by 2kaud View Post
    How many elements is comments going to have?
    At the moment it has about 130. That is probably about what it will end up with.

    Quote Originally Posted by 2kaud View Post
    Are you still using C++98? If yes, be aware of the performance issues returning a container from a function - as this will probably be by value (ie copied).
    Does having the map loading function return a reference to the map solve this, or am I better off declaring the map in the calling function and passing a reference to the loading function?

    LMHmedchem

  8. #8
    Join Date
    May 2009
    Location
    Boston
    Posts
    364

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    Oddly enough, it is working now. I am not sure what I did but now the map is being read correctly using your suggested modifications.

    LMHmedchem

  9. #9
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    6,977

    Re: bizare compiler error assigning a value to a string (expected constructor, etc)

    As comment_lookup_map is a const (correct), then the iterators need to be const as well - otherwise it would allow changes to a const variable!

    Just a small extra point, get_comment_lookup_map() could be defined as:

    Code:
    const map<string, string>& get_comment_lookup_map() {
    Does having the map loading function return a reference to the map solve this
    Yes. That's why I changed the function etc.

    I don't know how to print map elements without an iterator, so I don't know how to test this.
    Use C++17 - it's trivial (see my display loop in post #5). Or at least C++11 using auto.

    If the size of the map is as expected, but a find doesn't work when it should - then the issue is usually the find key not being correct (extra space, /n at end etc etc). Your find() references trimmed_line so I expect the answer to lie there.

    A C++98 method of displaying a map without using iterators is:

    Code:
    void display_map(const pair<string, string>& mapval)
    {
    	cout << mapval.first << '\t' << mapval.second << '\n';
    }
    
    ...
    
    for_each(comment_lookup_map.begin(), comment_lookup_map.end(), display_map);
    Last edited by 2kaud; December 17th, 2019 at 05:27 AM.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++17 Compiler: Microsoft VS2019 (16.4.5)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width




On-Demand Webinars (sponsored)