Conditionally use function with templates

Generic sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <string>
#include <type_traits>
#include <cuchar>

 
template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char16_t>
using Function = std::c16rtomb;

template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char32_t>
using Function = std::c32rtomb;

template<typename StringType>
std::string SomeFunction(const StringType& param)
{
	// Expected use:
        // Conditionally use different function
	Function<StringType>(/* args */);
}

// Expected use:
std::string32 test U"test";
SomeFunction(test);


I have never been good with templates as you can see.
What I'm trying to achieve is to use different function depending on what kind of a string is used.

How to do it with templates?
Last edited on
Do you mean something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <string>
#include <type_traits>
#include <cuchar>
#include <iostream>

template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char16_t> || std::is_same_v<typename StringType::value_type, char32_t>
void SomeFunction(const StringType& param) {
	if constexpr (std::is_same_v<typename StringType::value_type, char16_t>)
		//std::c16rtomb();
		std::cout << "Got char16\n";
	else
		//std::c32rtomb();
		std::cout << "Got char32\n";
}

int main() {
// Expected use:
	std::u32string test32 { U"test" };
	std::u16string test16 { u"test" };
	std::string test { "test" };

	SomeFunction(test32);
	SomeFunction(test16);
	//SomeFunction(test);  // Fails compilation
}


which displays:


Got char32
Got char16

Last edited on
Excelent!

In the mean time while struggling I come out with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<typename StringType, typename... Args>
using Function = std::size_t(*)(Args...);

template<typename StringType>
struct test
{
	Function<StringType> func = nullptr;
};

template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char16_t>
struct test<StringType>
{
	Function<StringType> func = std::c16rtomb;
};

template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char32_t>
struct test<StringType>
{
	Function<StringType> func = std::c32rtomb;
};


But with your help it's more readable and shorter to just write:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<typename StringType, typename... Args>
using Function = std::size_t(*)(Args...);

template<typename StringType>
requires std::is_same_v<typename StringType::value_type, char16_t> || std::is_same_v<typename StringType::value_type, char32_t>
void SomeFunction(const StringType& param)
{
	Function func = nullptr;

	if constexpr (std::is_same_v<typename StringType::value_type, char16_t>)
		func = std::c16rtomb;
	else if constexpr (std::is_same_v<typename StringType::value_type, char32_t>)
		func = std::c32rtomb();
	}

        // Use func
        func(/* args */)
}


Thank you a lot!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <concepts>
#include <cuchar>
#include <climits>
#include <ranges>

namespace internal
{
    template < typename CHAR_TYPE > std::string_view to_mb( CHAR_TYPE c )
        requires std::same_as<CHAR_TYPE,char32_t> || std::same_as<CHAR_TYPE,char16_t>
    {
        std::mbstate_t mbstate {};
        static char buff[MB_LEN_MAX] {};

        std::size_t num_bytes = -1U ;
        if constexpr ( std::same_as< CHAR_TYPE, char32_t > )
            num_bytes = std::c32rtomb( buff, c, std::addressof(mbstate) ) ;
        else if constexpr ( std::same_as< CHAR_TYPE, char16_t > )
            num_bytes = std::c16rtomb( buff, c, std::addressof(mbstate) ) ;

        return num_bytes != std::size_t(-1) ? std::string_view { buff, num_bytes } : "" ;
    }
}

template < std::ranges::input_range STR >
    requires requires { internal::to_mb( std::ranges::range_value_t<STR>{} ) ; }
[[nodiscard]] std::string to_narrow_str( STR& input_str )
{
    std::string result ;
    for( auto c : input_str ) result += internal::to_mb(c) ;
    return result ;
}

http://coliru.stacked-crooked.com/a/d5cd2d8bdfc613b1
JLBorges,

Interesting example I like it, the only problem is that conversion error should not imply skipping characters because this would result in truncated string, in other words it would be data loss.

For example if you read data from file and do conversion followed by writing converted data back to file then either entire data is converted or no conversion is made.

Therefore to_mb function should either throw or the design should be changed so that the result is an empty string.
1
2
3
4
5
6
7
8
9
10
11
12
13
template < std::ranges::input_range STR >
    requires requires { internal::to_mb( std::ranges::range_value_t<STR>{} ) ; }
[[nodiscard]] std::string to_narrow_str( STR& input_str )
{
    std::string result ;
    for( auto c : input_str )
    {
        const auto mb_view = internal::to_mb(c) ;
        if( mb_view.empty() ) return {} ; // conversion error: return an empty string.
        result += mb_view ;
    }
    return result ;
}
Registered users can post here. Sign in or register to post.