fix: change the behavior of printf in string op.
- add compiler hint for checking the arguments of printf. - change the return value of printf. from std::expected to normal value. use C++ exception to indicate error. * the error of printf usually caused by programmer. so it can be found when testing program. * so i use std::logic_error to indicate this and programmer should fix this before releasing program. - change the use of encoding convertion. for those cases that convertion must be safe, we unwrap it directly.
This commit is contained in:
@ -1,19 +1,21 @@
|
||||
#include "op.hpp"
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace yycc::string::op {
|
||||
|
||||
#pragma region Printf VPrintf
|
||||
|
||||
/// @brief The concept for all viable char type in printf function family
|
||||
template<typename TChar>
|
||||
requires(sizeof(TChar) == sizeof(char))
|
||||
static FormatResult<std::basic_string<TChar>> generic_printf(const TChar* format, va_list argptr) {
|
||||
concept PrintfSupportedChar = std::is_same_v<TChar, char> || std::is_same_v<TChar, char8_t>;
|
||||
|
||||
template<PrintfSupportedChar TChar>
|
||||
static std::basic_string<TChar> generic_printf(const TChar* format, va_list argptr) {
|
||||
// Prepare result
|
||||
std::basic_string<TChar> rv;
|
||||
|
||||
// Check format
|
||||
if (format == nullptr) return std::unexpected(FormatError::NullFormat);
|
||||
|
||||
// Prepare variable arguments
|
||||
va_list args1;
|
||||
va_copy(args1, argptr);
|
||||
@ -21,12 +23,13 @@ namespace yycc::string::op {
|
||||
va_copy(args2, argptr);
|
||||
|
||||
// The return value is desired char count without NULL terminal.
|
||||
// Minus number means error.
|
||||
// Negative number means error.
|
||||
int count = std::vsnprintf(nullptr, 0, reinterpret_cast<const char*>(format), args1);
|
||||
// Check expected size.
|
||||
if (count < 0) {
|
||||
// Invalid length returned by vsnprintf.
|
||||
return std::unexpected(FormatError::NoDesiredSize);
|
||||
// This may be caused by invalid format string
|
||||
throw std::logic_error("fail to determine the size of formatted string");
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
@ -39,14 +42,15 @@ namespace yycc::string::op {
|
||||
// Check written size.
|
||||
if (write_result < 0 || write_result > count) {
|
||||
// Invalid write result in vsnprintf.
|
||||
return std::unexpected(FormatError::BadWrittenSize);
|
||||
// Idk why this can happen.
|
||||
throw std::logic_error("the size of written formatted string is not expected");
|
||||
}
|
||||
|
||||
// Return value
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::u8string> printf(const char8_t* format, ...) {
|
||||
std::u8string printf(const char8_t* format, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
auto rv = vprintf(format, argptr);
|
||||
@ -54,11 +58,11 @@ namespace yycc::string::op {
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::u8string> vprintf(const char8_t* format, va_list argptr) {
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr) {
|
||||
return generic_printf(format, argptr);
|
||||
}
|
||||
|
||||
FormatResult<std::string> printf(const char* format, ...) {
|
||||
std::string printf(const char* format, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
auto rv = vprintf(format, argptr);
|
||||
@ -66,7 +70,7 @@ namespace yycc::string::op {
|
||||
return rv;
|
||||
}
|
||||
|
||||
FormatResult<std::string> vprintf(const char* format, va_list argptr) {
|
||||
std::string vprintf(const char* format, va_list argptr) {
|
||||
return generic_printf(format, argptr);
|
||||
}
|
||||
|
||||
|
@ -1,50 +1,41 @@
|
||||
#pragma once
|
||||
#include "../macro/printf_checker.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdarg>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <expected>
|
||||
|
||||
namespace yycc::string::op {
|
||||
|
||||
enum class FormatError {
|
||||
NullFormat, ///< Given format string is nullptr.
|
||||
NoDesiredSize, ///< Fail to fetch the expected size of result.
|
||||
BadWrittenSize, ///< The written size is different with expected size.
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using FormatResult = std::expected<T, FormatError>;
|
||||
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] ... Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::u8string> printf(const char8_t* format, ...);
|
||||
std::u8string printf(YYCC_PRINTF_CHECK_FMTSTR const char8_t* format, ...) YYCC_PRINTF_CHECK_ATTR(1, 2);
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] argptr Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::u8string> vprintf(const char8_t* format, va_list argptr);
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr);
|
||||
/**
|
||||
* @brief Perform an ordinary string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] ... Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::string> printf(const char* format, ...);
|
||||
std::string printf(YYCC_PRINTF_CHECK_FMTSTR const char* format, ...) YYCC_PRINTF_CHECK_ATTR(1, 2);
|
||||
/**
|
||||
* @brief Perform an ordinary string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] argptr Argument list of format string.
|
||||
* @return The formatted result, or the fail reason.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
FormatResult<std::string> vprintf(const char* format, va_list argptr);
|
||||
std::string vprintf(const char* format, va_list argptr);
|
||||
|
||||
/**
|
||||
* @brief Modify given string with all occurrences of substring \e old replaced by \e new.
|
||||
|
Reference in New Issue
Block a user