CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13
  1. #1
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,825

    Structured binding as initialisation

    In this example code

    Code:
    string newfile()
    {
    	const string filstem = "test"s;
    	const size_t fnumstart = 0U;
    
    	string filnam;
    	size_t fnum = fnumstart;
    
    	for (ifstream ifs; ifs.open(filnam = filstem + (fnum ? to_string(fnum) : "")), ifs.is_open(); ifs.close(), ++fnum);
    
    	return filnam;
    }
    The variable fnum is only used within the for loop - and hence ideally it's scope should be thus limited. However the init-statement element of a for (and with c++17 if and switch) statement only allows one type to be specified.

    c++17 introduced structured bindings, primarily for use with function return values. However, this concept can also be used to initialise multiple variables of different types. Consider

    Code:
    string newfile()
    {
    	const string filstem = "test"s;
    	const size_t fnumstart = 0U;
    
    	string filnam;
    
    	for (auto[fnum, ifs] = make_tuple(fnumstart, ifstream()); ifs.open(filnam = filstem + (fnum ? to_string(fnum) : "")), ifs.is_open(); ifs.close(), ++fnum);
    
    	return filnam;
    }
    Now the variables fnum and ifs are limited in scope as required.

    My question is this. Is there a 'better' way of accomplishing this?
    Last edited by 2kaud; January 4th, 2018 at 04:18 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++23 Compiler: Microsoft VS2022 (17.6.5)

  2. #2
    Join Date
    Feb 2017
    Posts
    677

    Re: Structured binding as initialisation

    Quote Originally Posted by 2kaud View Post
    My question is this. Is there a 'better' way of accomplishing this?
    I wouldn't say it's better but as an alternative you could introduce an outer scope to the for-loop, like

    Code:
    string newfile()
    {
    	const string filstem = "test"s;
    	const size_t fnumstart = 0U;
    
    	string filnam;
    
            { // for-loop outer scope
    		size_t fnum = fnumstart;
    
    		for (ifstream ifs; ifs.open(filnam = filstem + (fnum ? to_string(fnum) : "")), ifs.is_open(); ifs.close(), ++fnum);
    	}
    
    	return filnam;
    }
    Actually in the C++ standard a for-loop is defined to be equivalent to a while-loop with an outer scope like above, so it's an established usage. I use it quite often especially in association with scoped locks but also to split up Direct3D functions (that tend to be long and tedious).

    Your for-loop looks a little too crammed for my taste. I would probably try an algorithm and a lambda expression.

    When using a for loop I think it should be kept very simple. I probably would loop over fnum and do something very traditional like this.

    Code:
    string newfile2()
    {
    	const string filstem = "test";
    	const size_t fnumstart = 0U;
    	const size_t MAX_FNUM = 1000; // max number of files
    
    	ifstream ifs;
    	string filnam = filstem;
    	for (size_t fnum=fnumstart; fnum<MAX_FNUM; ++fnum) {
    		ifs.open(filnam);
    		if (ifs.is_open()) {
    			ifs.close();
    			return filnam; // a file was found - return
    		}
    		filnam = filstem + to_string(fnum);
    	}
    	return ""; // error
    }
    Last edited by wolle; January 4th, 2018 at 10:51 AM.

  3. #3
    Join Date
    Jun 2003
    Location
    Armenia, Yerevan
    Posts
    720

    Re: Structured binding as initialisation

    I used to meet such a question on interviews, my point was "as soon as the variable doesn't redefine another variable the traditional way of doing this is the way to go.". The issue needs to be investigated with the inclination of performance, which could be easily checked out with help of profiler.

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

    Re: Structured binding as initialisation

    What is probably really wanted is the ability to have a definition statement like

    Code:
    char a, int b = 0, c = 0, string d, ifstream e;
    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++23 Compiler: Microsoft VS2022 (17.6.5)

  5. #5
    Join Date
    Jun 2003
    Location
    Armenia, Yerevan
    Posts
    720

    Re: Structured binding as initialisation

    As far as I'm concerned, modern C++ doesn't allow to define multiple types in a for loop, so your solution with the tuple would flawlessly work out in this case. Probably, this issue would be eliminated in the upcoming editions of C++ standard.

  6. #6
    Join Date
    Feb 2017
    Posts
    677

    Re: Structured binding as initialisation

    Well, for readability reasons I don't think for-loops should be too crammed. And as we all know, syntax cramming (aka one-liners) don't make code more efficient.

    But okay, you could collect the multiple variable types in a struct. The struct could then be used as the loop variable in a for-loop.
    Last edited by wolle; January 5th, 2018 at 04:58 AM.

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

    Re: Structured binding as initialisation

    Code:
    string newfile2()
    {
    	const string filstem = "test";
    	const size_t fnumstart = 0U;
    	const size_t MAX_FNUM = 1000; // max number of files
    
    	ifstream ifs;
    	string filnam = filstem;
    	for (size_t fnum=fnumstart; fnum<MAX_FNUM; ++fnum) {
    		ifs.open(filnam);
    		if (ifs.is_open()) {
    			ifs.close();
    			return filnam; // a file was found - return
    		}
    		filnam = filstem + to_string(fnum);
    	}
    	return ""; // error
    }
    but this has the same issue re variable scope for ifs. Also don't you mean

    Code:
    string newfile2()
    {
    	const string filstem = "test";
    	const size_t fnumstart = 0U;
    	const size_t MAX_FNUM = 1000U; // max number of files
    
    	ifstream ifs;
    	string filnam = filstem;
    	for (size_t fnum = fnumstart; fnum < MAX_FNUM; ++fnum) {
    		ifs.open(filnam);
    		if (!ifs.is_open())
    			return filnam;    // a file was not found - return
    
                    ifs.close();
    		filnam = filstem + to_string(fnum);
    	}
    	return ""; // error
    }
    so that the for loop continues while files are found?
    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++23 Compiler: Microsoft VS2022 (17.6.5)

  8. #8
    Join Date
    Jan 2015
    Posts
    16

    Re: Structured binding as initialisation

    Have you considered using std::filesystem instead of the fstream?

  9. #9
    Join Date
    Feb 2017
    Posts
    677

    Re: Structured binding as initialisation

    Quote Originally Posted by 2kaud View Post
    but this has the same issue re variable scope for ifs.
    I thought about including my suggestion of an outer scope but since it had no meaning I left it out. Anyway it's easy to put one in anywhere you want it and it would look like this,

    Code:
    string newfile2()
    {
    	const string filstem = "test";
    	const size_t fnumstart = 0U;
    	const size_t MAX_FNUM = 1000; // max number of files
    
    	{ // for-loop scope
    		ifstream ifs;
    		string filnam = filstem;
    		for (size_t fnum=fnumstart; fnum<MAX_FNUM; ++fnum) {
    			ifs.open(filnam);
    			if (ifs.is_open()) {
    				ifs.close();
    				return filnam; // a file was found - return
    			}
    			filnam = filstem + to_string(fnum);
    		}
    	}
    	return ""; // error
    }
    Also don't you mean [... source code omited ...] so that the for loop continues while files are found?
    No I interpreted your function to mean that when an existing test file was found its name should be returned, otherwise "" should be returned. That's why I introduced an upper limit to how many test files names should be looked for. Without it the loop may potentially never end.

    Isn't that what you intended?

    The actual workings of your function may not matter though since I assume it serves as a mere backdrop for your question.
    Last edited by wolle; January 6th, 2018 at 05:35 AM.

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

    Re: Structured binding as initialisation

    I assume it serves a mere backdrop for your question
    Have you considered using std::filesystem instead of the fstream?
    This thread is about multi-variable initialisation of different types within a loop. The code given in post #1 is just an example from post #9 of this thread http://forums.codeguru.com/showthrea...le-name-change which illustrates the issue.

    Yes, in this example the c++ filesystem could be used. Consider

    Code:
    string newfile()
    {
    	const string filstem = "test"s;
    	const size_t fnumstart = 0U;
    
    	string filnam;
    
    	for(size_t fnum = fnumstart; exists(filnam = filstem + (fnum ? to_string(fnum) : "")); ++fnum);
    
    	return filnam;
    }
    which simplifies the code. However, in VS the namespace is std::experimental::filesystem - and I'm not sure about using 'experimental' code in production programs?
    Last edited by 2kaud; January 6th, 2018 at 05:59 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++23 Compiler: Microsoft VS2022 (17.6.5)

  11. #11
    Join Date
    Feb 2017
    Posts
    677

    Re: Structured binding as initialisation

    Quote Originally Posted by 2kaud View Post
    This thread is about multi-variable initialisation of different types within a loop.
    From what I can see there are two options, either use a Tuple as you did in #1, or use a struct (or class) as I suggested in #6.

    BUT if the sole purpose is to get variables inside the scope of a for-loop I would prefer to introduce an outer "two-braces" scope as I suggested in #2. This allows the for-loop to be kept simple and straight-forward.
    Last edited by wolle; January 7th, 2018 at 06:12 AM.

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

    Re: Structured binding as initialisation

    From the code in post #1

    Code:
    for (auto[fnum, ifs] = make_tuple(fnumstart, ifstream()); ifs.open(filnam = filstem + (fnum ? to_string(fnum) : "")), ifs.is_open(); ifs.close(), ++fnum);
    with VS2017 15.7.0 this can be now slightly simplified to

    Code:
    for (auto[fnum, ifs] = tuple(fnumstart, ifstream()); ifs.open(filnam = filstem + (fnum ? to_string(fnum) : "")), ifs.is_open(); ifs.close(), ++fnum);
    as VS2017 15.7.0 now supports c++17 Template argument deduction for class templates. So the templated class names can be used directly without having to use the 'make_...' versions or to supply template type parameters. Finally!
    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++23 Compiler: Microsoft VS2022 (17.6.5)

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

    Re: Structured binding as initialisation

    However, in VS the namespace is std::experimental::filesystem - and I'm not sure about using 'experimental' code in production programs?
    As of VS 2017 15.7.1, filesystem is now in std namespace. Note that this is a new implementation version of filesystem and may not be always compatible with the previous experimental::filesystem, although experimental::filesystem is still available if needed but shouldn't be used unless absolutely necessary.
    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++23 Compiler: Microsoft VS2022 (17.6.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
  •  





Click Here to Expand Forum to Full Width

Featured