I am learning C++ and currently playing with variadic template functions and formatting string, So here is my small attempt to write a formatting function and provide a simple iostream-like interface for std::FILE, I am using '%' as a format specifier f.e
format("const CharT *str") == "const CharT *str"
format("HelloWorld - %", 10) == "HelloWorld - 10"
here is the code:
#include <cstdlib>
#include <string>
namespace gupta {
using string_t = std::string;
using CharT = string_t::value_type;
inline string_t format(const CharT *str) { return str; }
template <typename T> inline string_t to_string(const T &f) { return std::to_string(f); }
inline string_t to_string(const char *str) { return std::string{str}; }
inline string_t to_string(string_t s) { return (s); }
template <typename T, typename... Ts>
string_t format(const CharT *str, const T &arg, const Ts &... args) {
string_t res;
for(; *str; str++) {
if(*str == '%') {
if(*(str + 1) == '%') {
str++;
}
else {
res += to_string(arg);
return res += format(str + 1, args...);
}
}
res += *str;
}
return res;
}
template <typename... Ts> auto fprint(std::FILE *f, const char *str, Ts &&... args) {
auto s = std::move(format(str, std::forward<Ts>(args)...));
return fwrite(s.data(), 1, s.size(), f);
}
template <typename... Ts> inline auto print(const char *str, Ts &&... args) {
return fprint(stdout, str, std::forward<Ts>(args)...);
}
template <typename... Ts> inline auto debug(const char *str, Ts &&... args) {
return fprint(stderr, str, std::forward<Ts>(args)...);
}
namespace detail {
class _stdout_object {};
class _stderr_object {};
} // namespace detail
namespace printing_shortcuts {
template <typename T> detail::_stdout_object operator<<(detail::_stdout_object f, const T &arg) {
auto s = to_string(arg);
fwrite(s.data(), 1, s.size(), stdout);
return f;
}
template <typename T> detail::_stderr_object operator<<(detail::_stderr_object f, const T &arg) {
auto s = to_string(arg);
fwrite(s.data(), 1, s.size(), stderr);
return f;
}
detail::_stdout_object print() { return {}; }
detail::_stderr_object debug() { return {}; }
} // namespace printing_shortcuts
using namespace printing_shortcuts;
} // namespace gupta
using namespace gupta::printing_shortcuts;
class test {};
std::string to_string(const test &) { return "test"; }
#include <assert.h>
int main() {
using namespace gupta;
assert(format("const CharT *str") == "const CharT *str");
assert(format("HelloWorld - %", 10) == "HelloWorld - 10");
print("%s\n", format("HelloWorld - %,%", 10, test{}));
assert(format("HelloWorld - %,%", 10, test{}) == "HelloWorld - 10,test");
}
also, does it make sense to replace class _stdout_object
while their pointers with something like
namespace detail {
class _stdout_object;
class _stderr_object;
} // namespace detail
namespace printing_shortcuts {
template <typename T> detail::_stdout_object *operator<<(detail::_stdout_object *, const T &arg) {
auto s = to_string(arg);
fwrite(s.data(), 1, s.size(), stdout);
return nullptr;
}
template <typename T> detail::_stderr_object *operator<<(detail::_stderr_object *, const T &arg) {
auto s = to_string(arg);
fwrite(s.data(), 1, s.size(), stderr);
return nullptr;
}
detail::_stdout_object *print() { return nullptr; }
detail::_stderr_object *debug() { return nullptr; }
} // namespace printing_shortcuts
printf()
implementation. github.com/Loki-Astari/ThorsIOUtil/blob/master/src/ThorsIOUtil/… \$\endgroup\$