A string_splitter using C++17any_function using the upcoming std::any“Multi” scope guard using template...

Why doesn't "auto ch = unsigned char{'p'}" compile under C++ 17?

Why does String.replaceAll() work differently in Java 8 from Java 9?

How do I say "Brexit" in Latin?

How to tag distinct options/entities without giving any an implicit priority or suggested order?

Jumping Numbers

What to do when being responsible for data protection in your lab, yet advice is ignored?

Why Smart Plugs don't require setting port forwarding on router and how to accomplish this with NodeMCU (ESP8266)

The effects of magnetism in radio transmissions

How do you funnel food off a cutting board?

Is a debit card dangerous for an account with low balance and no overdraft protection?

Every character has a name - does this lead to too many named characters?

Does Windows 10's telemetry include sending *.doc files if Word crashed?

What makes the Forgotten Realms "forgotten"?

Is it a fallacy if someone claims they need an explanation for every word of your argument to the point where they don't understand common terms?

Word or phrase for showing great skill at something without formal training in it

Isn't using the Extrusion Multiplier like cheating?

Avoiding morning and evening handshakes

How does Arcane Armament interact with the Artillerist Artificer's Wand Prototype feature?

What is this metal M-shaped device for?

If I sold a PS4 game I owned the disc for, can I reinstall it digitally?

Disable the ">" operator in Rstudio linux terminal

What is the purpose of easy combat scenarios that don't need resource expenditure?

Broken patches on a road

Am I a Rude Number?



A string_splitter using C++17


any_function using the upcoming std::any“Multi” scope guard using template argument deducing featureEvent handling attempt using some of the c++ 17 featuresBinary search tree in C++ 17 using iterator patternCreate a C++ string using printf-style formattingWrap pattern “using std::begin; return begin(c);” into a functionASCII to uint16 using fallthroughUsing multiple console windows for outputTravelling salesman problem using genetic algorithm in C++Qsort in C++ using vector













6












$begingroup$


In this repo I've put together a header only string splitter, allowing for characters and string literals as delimiters.



The (little) library is strictly C++17.



I would like to ask for your comments.



I would also like the code to be as short as possible, so any comments in that direction are also most welcome.



As it appears to be mandatory to include at least 3 lines of code, here's the code:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

std::ostream & nl ( std::ostream & out_ ) { return out_ << 'n'; }
std::wostream & nl ( std::wostream & out_ ) { return out_ << L'n'; }

namespace sax::detail {

template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {
return x; // guaranteed copy elision.
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {
return std::basic_string_view<CharT> ( std::addressof ( x ), 1 );
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {
return std::basic_string_view<CharT> ( x );
}


template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {
s.remove_prefix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
if ( s.size ( ) >= 1 and s [ 0 ] == x ) {
s.remove_prefix ( 1 );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {
s.remove_suffix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) );
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT, typename SizeT, typename StringyThing>
constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {
f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ );
}
template<typename CharT, typename ... Args>
[[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
auto found = std::basic_string_view<CharT>::npos;
( find ( s_, found, std::forward<Args> ( args_ ) ), ... );
return found;
}

}

namespace sax {

template<typename CharT, typename ... Delimiters>
[[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {
using size_type = typename std::basic_string_view<CharT>::size_type;
std::basic_string_view<CharT> string_view ( string_ );
std::vector<std::basic_string_view<CharT>> string_view_vector;
string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.
// Remove trailing delimiters.
detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
// Parse the string_view left to right.
while ( true ) {
detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
if ( std::basic_string_view<CharT>::npos == pos ) {
string_view_vector.emplace_back ( std::move ( string_view ) );
break;
}
string_view_vector.emplace_back ( string_view.data ( ), pos );
string_view.remove_prefix ( pos );
}
return string_view_vector;
}

}


Used like so:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>

#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>

namespace fs = std::filesystem;

#include <string_split.hpp>


template<typename Stream, typename Container>
Stream & operator << ( Stream & out_, const Container & s_ ) noexcept {
for ( const auto & v : s_ )
out_ << '"' << v << "" ";
out_ << 'b';
return out_;
}


int main ( ) {

std::string s ( " , t the quick brown ,, ,fox jumps underover t , the lazy dog ," );

std::cout << sax::string_split ( s, " ", ',', "t", "under" ) << nl;

return EXIT_SUCCESS;
}


Output:



"the" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog"









share|improve this question









New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$












  • $begingroup$
    Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
    $endgroup$
    – 422_unprocessable_entity
    10 hours ago






  • 1




    $begingroup$
    @422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
    $endgroup$
    – degski
    10 hours ago












  • $begingroup$
    @degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
    $endgroup$
    – user673679
    9 hours ago










  • $begingroup$
    Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
    $endgroup$
    – user786653
    7 hours ago
















6












$begingroup$


In this repo I've put together a header only string splitter, allowing for characters and string literals as delimiters.



The (little) library is strictly C++17.



I would like to ask for your comments.



I would also like the code to be as short as possible, so any comments in that direction are also most welcome.



As it appears to be mandatory to include at least 3 lines of code, here's the code:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

std::ostream & nl ( std::ostream & out_ ) { return out_ << 'n'; }
std::wostream & nl ( std::wostream & out_ ) { return out_ << L'n'; }

namespace sax::detail {

template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {
return x; // guaranteed copy elision.
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {
return std::basic_string_view<CharT> ( std::addressof ( x ), 1 );
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {
return std::basic_string_view<CharT> ( x );
}


template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {
s.remove_prefix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
if ( s.size ( ) >= 1 and s [ 0 ] == x ) {
s.remove_prefix ( 1 );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {
s.remove_suffix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) );
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT, typename SizeT, typename StringyThing>
constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {
f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ );
}
template<typename CharT, typename ... Args>
[[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
auto found = std::basic_string_view<CharT>::npos;
( find ( s_, found, std::forward<Args> ( args_ ) ), ... );
return found;
}

}

namespace sax {

template<typename CharT, typename ... Delimiters>
[[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {
using size_type = typename std::basic_string_view<CharT>::size_type;
std::basic_string_view<CharT> string_view ( string_ );
std::vector<std::basic_string_view<CharT>> string_view_vector;
string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.
// Remove trailing delimiters.
detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
// Parse the string_view left to right.
while ( true ) {
detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
if ( std::basic_string_view<CharT>::npos == pos ) {
string_view_vector.emplace_back ( std::move ( string_view ) );
break;
}
string_view_vector.emplace_back ( string_view.data ( ), pos );
string_view.remove_prefix ( pos );
}
return string_view_vector;
}

}


Used like so:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>

#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>

namespace fs = std::filesystem;

#include <string_split.hpp>


template<typename Stream, typename Container>
Stream & operator << ( Stream & out_, const Container & s_ ) noexcept {
for ( const auto & v : s_ )
out_ << '"' << v << "" ";
out_ << 'b';
return out_;
}


int main ( ) {

std::string s ( " , t the quick brown ,, ,fox jumps underover t , the lazy dog ," );

std::cout << sax::string_split ( s, " ", ',', "t", "under" ) << nl;

return EXIT_SUCCESS;
}


Output:



"the" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog"









share|improve this question









New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$












  • $begingroup$
    Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
    $endgroup$
    – 422_unprocessable_entity
    10 hours ago






  • 1




    $begingroup$
    @422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
    $endgroup$
    – degski
    10 hours ago












  • $begingroup$
    @degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
    $endgroup$
    – user673679
    9 hours ago










  • $begingroup$
    Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
    $endgroup$
    – user786653
    7 hours ago














6












6








6





$begingroup$


In this repo I've put together a header only string splitter, allowing for characters and string literals as delimiters.



The (little) library is strictly C++17.



I would like to ask for your comments.



I would also like the code to be as short as possible, so any comments in that direction are also most welcome.



As it appears to be mandatory to include at least 3 lines of code, here's the code:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

std::ostream & nl ( std::ostream & out_ ) { return out_ << 'n'; }
std::wostream & nl ( std::wostream & out_ ) { return out_ << L'n'; }

namespace sax::detail {

template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {
return x; // guaranteed copy elision.
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {
return std::basic_string_view<CharT> ( std::addressof ( x ), 1 );
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {
return std::basic_string_view<CharT> ( x );
}


template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {
s.remove_prefix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
if ( s.size ( ) >= 1 and s [ 0 ] == x ) {
s.remove_prefix ( 1 );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {
s.remove_suffix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) );
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT, typename SizeT, typename StringyThing>
constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {
f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ );
}
template<typename CharT, typename ... Args>
[[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
auto found = std::basic_string_view<CharT>::npos;
( find ( s_, found, std::forward<Args> ( args_ ) ), ... );
return found;
}

}

namespace sax {

template<typename CharT, typename ... Delimiters>
[[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {
using size_type = typename std::basic_string_view<CharT>::size_type;
std::basic_string_view<CharT> string_view ( string_ );
std::vector<std::basic_string_view<CharT>> string_view_vector;
string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.
// Remove trailing delimiters.
detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
// Parse the string_view left to right.
while ( true ) {
detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
if ( std::basic_string_view<CharT>::npos == pos ) {
string_view_vector.emplace_back ( std::move ( string_view ) );
break;
}
string_view_vector.emplace_back ( string_view.data ( ), pos );
string_view.remove_prefix ( pos );
}
return string_view_vector;
}

}


Used like so:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>

#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>

namespace fs = std::filesystem;

#include <string_split.hpp>


template<typename Stream, typename Container>
Stream & operator << ( Stream & out_, const Container & s_ ) noexcept {
for ( const auto & v : s_ )
out_ << '"' << v << "" ";
out_ << 'b';
return out_;
}


int main ( ) {

std::string s ( " , t the quick brown ,, ,fox jumps underover t , the lazy dog ," );

std::cout << sax::string_split ( s, " ", ',', "t", "under" ) << nl;

return EXIT_SUCCESS;
}


Output:



"the" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog"









share|improve this question









New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$




In this repo I've put together a header only string splitter, allowing for characters and string literals as delimiters.



The (little) library is strictly C++17.



I would like to ask for your comments.



I would also like the code to be as short as possible, so any comments in that direction are also most welcome.



As it appears to be mandatory to include at least 3 lines of code, here's the code:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

std::ostream & nl ( std::ostream & out_ ) { return out_ << 'n'; }
std::wostream & nl ( std::wostream & out_ ) { return out_ << L'n'; }

namespace sax::detail {

template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {
return x; // guaranteed copy elision.
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {
return std::basic_string_view<CharT> ( std::addressof ( x ), 1 );
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {
return std::basic_string_view<CharT> ( x );
}


template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {
s.remove_prefix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
if ( s.size ( ) >= 1 and s [ 0 ] == x ) {
s.remove_prefix ( 1 );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {
s.remove_suffix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) );
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}


template<typename CharT, typename SizeT, typename StringyThing>
constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {
f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ );
}
template<typename CharT, typename ... Args>
[[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
auto found = std::basic_string_view<CharT>::npos;
( find ( s_, found, std::forward<Args> ( args_ ) ), ... );
return found;
}

}

namespace sax {

template<typename CharT, typename ... Delimiters>
[[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {
using size_type = typename std::basic_string_view<CharT>::size_type;
std::basic_string_view<CharT> string_view ( string_ );
std::vector<std::basic_string_view<CharT>> string_view_vector;
string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.
// Remove trailing delimiters.
detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
// Parse the string_view left to right.
while ( true ) {
detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
if ( std::basic_string_view<CharT>::npos == pos ) {
string_view_vector.emplace_back ( std::move ( string_view ) );
break;
}
string_view_vector.emplace_back ( string_view.data ( ), pos );
string_view.remove_prefix ( pos );
}
return string_view_vector;
}

}


Used like so:



// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>

#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>

namespace fs = std::filesystem;

#include <string_split.hpp>


template<typename Stream, typename Container>
Stream & operator << ( Stream & out_, const Container & s_ ) noexcept {
for ( const auto & v : s_ )
out_ << '"' << v << "" ";
out_ << 'b';
return out_;
}


int main ( ) {

std::string s ( " , t the quick brown ,, ,fox jumps underover t , the lazy dog ," );

std::cout << sax::string_split ( s, " ", ',', "t", "under" ) << nl;

return EXIT_SUCCESS;
}


Output:



"the" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog"






c++ strings c++17






share|improve this question









New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 9 hours ago







degski













New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 11 hours ago









degskidegski

1313




1313




New contributor




degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






degski is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • $begingroup$
    Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
    $endgroup$
    – 422_unprocessable_entity
    10 hours ago






  • 1




    $begingroup$
    @422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
    $endgroup$
    – degski
    10 hours ago












  • $begingroup$
    @degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
    $endgroup$
    – user673679
    9 hours ago










  • $begingroup$
    Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
    $endgroup$
    – user786653
    7 hours ago


















  • $begingroup$
    Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
    $endgroup$
    – 422_unprocessable_entity
    10 hours ago






  • 1




    $begingroup$
    @422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
    $endgroup$
    – degski
    10 hours ago












  • $begingroup$
    @degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
    $endgroup$
    – user673679
    9 hours ago










  • $begingroup$
    Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
    $endgroup$
    – user786653
    7 hours ago
















$begingroup$
Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
$endgroup$
– 422_unprocessable_entity
10 hours ago




$begingroup$
Welcome to CR. This is very interesting looking code. Hope you learn a lot here.
$endgroup$
– 422_unprocessable_entity
10 hours ago




1




1




$begingroup$
@422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
$endgroup$
– degski
10 hours ago






$begingroup$
@422_unprocessable_entity Thanks, I do hope to learn something, it's all about exploring C++17.
$endgroup$
– degski
10 hours ago














$begingroup$
@degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
$endgroup$
– user673679
9 hours ago




$begingroup$
@degski Be aware that any code posted to CodeReview is licensed under the "CC BY-SA 3.0" license: codereview.stackexchange.com/help/licensing . (So the MIT license is really just taking up extra space in the code listing).
$endgroup$
– user673679
9 hours ago












$begingroup$
Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
$endgroup$
– user786653
7 hours ago




$begingroup$
Isn't make_string_view ( CharT x ) returning a pointer to a temporary (the argument)? removed = removed or true should just be removed = true, nl doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases.
$endgroup$
– user786653
7 hours ago










1 Answer
1






active

oldest

votes


















2












$begingroup$

I don't always follow this advice myself, but it's a good idea to write
a specification for the function/algorithm you're implementing. (See
this
post by Eric Lippert for more information). That'll also make it easier
to come up with test cases and reason about various corner cases (like
what's the behavior if I try do split("aaba", "a", "ab") for
instance?).



In this case we might go for something like: Given a string_view and a
list (argument list) of "string like" delimiters return a std::vector
of string_views representing the text between delimiters (using the
first matching delimiter). If no delimiters are found return a vector
with one element consisting of the input string.



I mention this because at the moment it's a bit hard to see the overall
idea behind your code. It's not totally clear to me what it's doing, I
can see it's removing suffixes and prefixes, but it's hard to verify
that the main loop always terminates for instance. I think if you'd
written an informal specification beforehand you'd probably have ended
up with a more structured and easier to understand main loop.



Here's a list of some other things I noticed while reading the code
(some are from my comment):





  • make_string_view( CharT X ) returns a pointer to a temporary.
    You can make a string_view that way from the arguments, but you
    need pass the argument by reference all the way.


  • removed = removed or true should just be removed = true


  • nl doesn't belong in that header

  • I'd put all internal functions into some kind of
    private/internal/detail namespace

  • I personally only use [[nodiscard]] on functions where it's a
    big mistake to not use the return value (I don't think
    make_string_view qualifies)

  • You're missing && on the delimiters for std::forward to do its
    magic

  • You probably only want to create string_views from the delimiter
    arguments once


Given all of the above I image you could implement the function
something like this (in pseudo-code):



vector<string_view<CharT>> split(string_view<CharT> str, Delimiters&& delims...) {
if (str.empty()) return { str }; // Get special case out of the way
vector<string_view<CharT>> res;
size_t last_index = 0;
const auto delim_array = make_delim_array(std::forward<Delimiters>(delims)...);
for (size_t i = 0; i < str.length();) {
if (auto match_length = any_matches(str, i, delim_array)) {
res.push_back(str.substr(last_index, i - last_index));
i += match_length;
last_index = i;
} else {
++i;
}
}
res.push_back(str.substr(last_index));
return res;
}





share|improve this answer











$endgroup$













    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    degski is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214544%2fa-string-splitter-using-c17%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2












    $begingroup$

    I don't always follow this advice myself, but it's a good idea to write
    a specification for the function/algorithm you're implementing. (See
    this
    post by Eric Lippert for more information). That'll also make it easier
    to come up with test cases and reason about various corner cases (like
    what's the behavior if I try do split("aaba", "a", "ab") for
    instance?).



    In this case we might go for something like: Given a string_view and a
    list (argument list) of "string like" delimiters return a std::vector
    of string_views representing the text between delimiters (using the
    first matching delimiter). If no delimiters are found return a vector
    with one element consisting of the input string.



    I mention this because at the moment it's a bit hard to see the overall
    idea behind your code. It's not totally clear to me what it's doing, I
    can see it's removing suffixes and prefixes, but it's hard to verify
    that the main loop always terminates for instance. I think if you'd
    written an informal specification beforehand you'd probably have ended
    up with a more structured and easier to understand main loop.



    Here's a list of some other things I noticed while reading the code
    (some are from my comment):





    • make_string_view( CharT X ) returns a pointer to a temporary.
      You can make a string_view that way from the arguments, but you
      need pass the argument by reference all the way.


    • removed = removed or true should just be removed = true


    • nl doesn't belong in that header

    • I'd put all internal functions into some kind of
      private/internal/detail namespace

    • I personally only use [[nodiscard]] on functions where it's a
      big mistake to not use the return value (I don't think
      make_string_view qualifies)

    • You're missing && on the delimiters for std::forward to do its
      magic

    • You probably only want to create string_views from the delimiter
      arguments once


    Given all of the above I image you could implement the function
    something like this (in pseudo-code):



    vector<string_view<CharT>> split(string_view<CharT> str, Delimiters&& delims...) {
    if (str.empty()) return { str }; // Get special case out of the way
    vector<string_view<CharT>> res;
    size_t last_index = 0;
    const auto delim_array = make_delim_array(std::forward<Delimiters>(delims)...);
    for (size_t i = 0; i < str.length();) {
    if (auto match_length = any_matches(str, i, delim_array)) {
    res.push_back(str.substr(last_index, i - last_index));
    i += match_length;
    last_index = i;
    } else {
    ++i;
    }
    }
    res.push_back(str.substr(last_index));
    return res;
    }





    share|improve this answer











    $endgroup$


















      2












      $begingroup$

      I don't always follow this advice myself, but it's a good idea to write
      a specification for the function/algorithm you're implementing. (See
      this
      post by Eric Lippert for more information). That'll also make it easier
      to come up with test cases and reason about various corner cases (like
      what's the behavior if I try do split("aaba", "a", "ab") for
      instance?).



      In this case we might go for something like: Given a string_view and a
      list (argument list) of "string like" delimiters return a std::vector
      of string_views representing the text between delimiters (using the
      first matching delimiter). If no delimiters are found return a vector
      with one element consisting of the input string.



      I mention this because at the moment it's a bit hard to see the overall
      idea behind your code. It's not totally clear to me what it's doing, I
      can see it's removing suffixes and prefixes, but it's hard to verify
      that the main loop always terminates for instance. I think if you'd
      written an informal specification beforehand you'd probably have ended
      up with a more structured and easier to understand main loop.



      Here's a list of some other things I noticed while reading the code
      (some are from my comment):





      • make_string_view( CharT X ) returns a pointer to a temporary.
        You can make a string_view that way from the arguments, but you
        need pass the argument by reference all the way.


      • removed = removed or true should just be removed = true


      • nl doesn't belong in that header

      • I'd put all internal functions into some kind of
        private/internal/detail namespace

      • I personally only use [[nodiscard]] on functions where it's a
        big mistake to not use the return value (I don't think
        make_string_view qualifies)

      • You're missing && on the delimiters for std::forward to do its
        magic

      • You probably only want to create string_views from the delimiter
        arguments once


      Given all of the above I image you could implement the function
      something like this (in pseudo-code):



      vector<string_view<CharT>> split(string_view<CharT> str, Delimiters&& delims...) {
      if (str.empty()) return { str }; // Get special case out of the way
      vector<string_view<CharT>> res;
      size_t last_index = 0;
      const auto delim_array = make_delim_array(std::forward<Delimiters>(delims)...);
      for (size_t i = 0; i < str.length();) {
      if (auto match_length = any_matches(str, i, delim_array)) {
      res.push_back(str.substr(last_index, i - last_index));
      i += match_length;
      last_index = i;
      } else {
      ++i;
      }
      }
      res.push_back(str.substr(last_index));
      return res;
      }





      share|improve this answer











      $endgroup$
















        2












        2








        2





        $begingroup$

        I don't always follow this advice myself, but it's a good idea to write
        a specification for the function/algorithm you're implementing. (See
        this
        post by Eric Lippert for more information). That'll also make it easier
        to come up with test cases and reason about various corner cases (like
        what's the behavior if I try do split("aaba", "a", "ab") for
        instance?).



        In this case we might go for something like: Given a string_view and a
        list (argument list) of "string like" delimiters return a std::vector
        of string_views representing the text between delimiters (using the
        first matching delimiter). If no delimiters are found return a vector
        with one element consisting of the input string.



        I mention this because at the moment it's a bit hard to see the overall
        idea behind your code. It's not totally clear to me what it's doing, I
        can see it's removing suffixes and prefixes, but it's hard to verify
        that the main loop always terminates for instance. I think if you'd
        written an informal specification beforehand you'd probably have ended
        up with a more structured and easier to understand main loop.



        Here's a list of some other things I noticed while reading the code
        (some are from my comment):





        • make_string_view( CharT X ) returns a pointer to a temporary.
          You can make a string_view that way from the arguments, but you
          need pass the argument by reference all the way.


        • removed = removed or true should just be removed = true


        • nl doesn't belong in that header

        • I'd put all internal functions into some kind of
          private/internal/detail namespace

        • I personally only use [[nodiscard]] on functions where it's a
          big mistake to not use the return value (I don't think
          make_string_view qualifies)

        • You're missing && on the delimiters for std::forward to do its
          magic

        • You probably only want to create string_views from the delimiter
          arguments once


        Given all of the above I image you could implement the function
        something like this (in pseudo-code):



        vector<string_view<CharT>> split(string_view<CharT> str, Delimiters&& delims...) {
        if (str.empty()) return { str }; // Get special case out of the way
        vector<string_view<CharT>> res;
        size_t last_index = 0;
        const auto delim_array = make_delim_array(std::forward<Delimiters>(delims)...);
        for (size_t i = 0; i < str.length();) {
        if (auto match_length = any_matches(str, i, delim_array)) {
        res.push_back(str.substr(last_index, i - last_index));
        i += match_length;
        last_index = i;
        } else {
        ++i;
        }
        }
        res.push_back(str.substr(last_index));
        return res;
        }





        share|improve this answer











        $endgroup$



        I don't always follow this advice myself, but it's a good idea to write
        a specification for the function/algorithm you're implementing. (See
        this
        post by Eric Lippert for more information). That'll also make it easier
        to come up with test cases and reason about various corner cases (like
        what's the behavior if I try do split("aaba", "a", "ab") for
        instance?).



        In this case we might go for something like: Given a string_view and a
        list (argument list) of "string like" delimiters return a std::vector
        of string_views representing the text between delimiters (using the
        first matching delimiter). If no delimiters are found return a vector
        with one element consisting of the input string.



        I mention this because at the moment it's a bit hard to see the overall
        idea behind your code. It's not totally clear to me what it's doing, I
        can see it's removing suffixes and prefixes, but it's hard to verify
        that the main loop always terminates for instance. I think if you'd
        written an informal specification beforehand you'd probably have ended
        up with a more structured and easier to understand main loop.



        Here's a list of some other things I noticed while reading the code
        (some are from my comment):





        • make_string_view( CharT X ) returns a pointer to a temporary.
          You can make a string_view that way from the arguments, but you
          need pass the argument by reference all the way.


        • removed = removed or true should just be removed = true


        • nl doesn't belong in that header

        • I'd put all internal functions into some kind of
          private/internal/detail namespace

        • I personally only use [[nodiscard]] on functions where it's a
          big mistake to not use the return value (I don't think
          make_string_view qualifies)

        • You're missing && on the delimiters for std::forward to do its
          magic

        • You probably only want to create string_views from the delimiter
          arguments once


        Given all of the above I image you could implement the function
        something like this (in pseudo-code):



        vector<string_view<CharT>> split(string_view<CharT> str, Delimiters&& delims...) {
        if (str.empty()) return { str }; // Get special case out of the way
        vector<string_view<CharT>> res;
        size_t last_index = 0;
        const auto delim_array = make_delim_array(std::forward<Delimiters>(delims)...);
        for (size_t i = 0; i < str.length();) {
        if (auto match_length = any_matches(str, i, delim_array)) {
        res.push_back(str.substr(last_index, i - last_index));
        i += match_length;
        last_index = i;
        } else {
        ++i;
        }
        }
        res.push_back(str.substr(last_index));
        return res;
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 6 hours ago









        user673679

        3,26411129




        3,26411129










        answered 6 hours ago









        user786653user786653

        20115




        20115






















            degski is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            degski is a new contributor. Be nice, and check out our Code of Conduct.













            degski is a new contributor. Be nice, and check out our Code of Conduct.












            degski is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214544%2fa-string-splitter-using-c17%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Fairchild Swearingen Metro Inhaltsverzeichnis Geschichte | Innenausstattung | Nutzung | Zwischenfälle...

            Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...

            Marineschifffahrtleitung Inhaltsverzeichnis Geschichte | Heutige Organisation der NATO | Nationale und...