Skip to content
Snippets Groups Projects
Commit e2f48e19 authored by Sam Yates's avatar Sam Yates Committed by Ben Cumming
Browse files

Move string-printf utility from lmorpho into util. (#228)

Move string-printf utility from lmorpho into util.

* Add `util::strprintf` printf-alike function that returns a `std::string` result.
* Include simple adaptors for `std::string` and standard smart pointer arguments fot `util::strprintf` (i.e. `std::string` arguments can be used with `%s`, smart pointers with `%p`).
* Add unit tests to suit.
parent 6f6a2db6
No related branches found
No related tags found
No related merge requests found
...@@ -6,32 +6,15 @@ ...@@ -6,32 +6,15 @@
#include <morphology.hpp> #include <morphology.hpp>
#include <swcio.hpp> #include <swcio.hpp>
#include <util/strprintf.hpp>
#include "morphio.hpp" #include "morphio.hpp"
using nest::mc::io::swc_record; using nest::mc::io::swc_record;
using nest::mc::util::strprintf;
std::vector<swc_record> as_swc(const nest::mc::morphology& morph); std::vector<swc_record> as_swc(const nest::mc::morphology& morph);
// printf wrappers.
template <typename... Args>
static std::string strprintf(const char* fmt, Args&&... args) {
thread_local static std::vector<char> buffer(1024);
for (;;) {
int n = std::snprintf(buffer.data(), buffer.size(), fmt, std::forward<Args>(args)...);
if (n<0) return ""; // error
if ((unsigned)n<buffer.size()) return std::string(buffer.data());
buffer.resize(2*n);
}
}
template <typename... Args>
static std::string strprintf(std::string fmt, Args&&... args) {
return strprintf(fmt.c_str(), std::forward<Args>(args)...);
}
// Multi-file manager implementation. // Multi-file manager implementation.
multi_file::multi_file(const std::string& pattern, int digits) { multi_file::multi_file(const std::string& pattern, int digits) {
auto npos = std::string::npos; auto npos = std::string::npos;
......
#pragma once
/* Thin wrapper around std::snprintf for sprintf-like formatting
* to std::string. */
#include <cstdio>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace nest {
namespace mc {
namespace util {
namespace impl {
template <typename X>
X sprintf_arg_translate(const X& x) { return x; }
inline const char* sprintf_arg_translate(const std::string& x) { return x.c_str(); }
template <typename T, typename Deleter>
T* sprintf_arg_translate(const std::unique_ptr<T, Deleter>& x) { return x.get(); }
template <typename T>
T* sprintf_arg_translate(const std::shared_ptr<T>& x) { return x.get(); }
}
template <typename... Args>
std::string strprintf(const char* fmt, Args&&... args) {
thread_local static std::vector<char> buffer(1024);
for (;;) {
int n = std::snprintf(buffer.data(), buffer.size(), fmt, impl::sprintf_arg_translate(std::forward<Args>(args))...);
if (n<0) {
throw std::system_error(errno, std::generic_category());
}
else if ((unsigned)n<buffer.size()) {
return std::string(buffer.data(), n);
}
buffer.resize(2*n);
}
}
template <typename... Args>
std::string strprintf(const std::string& fmt, Args&&... args) {
return strprintf(fmt.c_str(), std::forward<Args>(args)...);
}
} // namespace util
} // namespace mc
} // namespace nest
...@@ -60,6 +60,7 @@ set(TEST_SOURCES ...@@ -60,6 +60,7 @@ set(TEST_SOURCES
test_spikes.cpp test_spikes.cpp
test_spike_store.cpp test_spike_store.cpp
test_stimulus.cpp test_stimulus.cpp
test_strprintf.cpp
test_swcio.cpp test_swcio.cpp
test_synapses.cpp test_synapses.cpp
test_tree.cpp test_tree.cpp
......
#include <cstdio>
#include <memory>
#include <string>
#include "../gtest.h"
#include <util/strprintf.hpp>
using namespace nest::mc::util;
TEST(strprintf, simple) {
char buf[200];
const char* fmt1 = " %% %04d % 3.2f %#016x %c";
sprintf(buf, fmt1, 3, 7.1e-3, 0x1234ul, 'x');
auto result = strprintf(fmt1, 3, 7.1e-3, 0x1234ul, 'x');
EXPECT_EQ(std::string(buf), result);
const char* fmt2 = "%5s %3s";
sprintf(buf, fmt2, "bear", "pear");
result = strprintf(fmt2, "bear", "pear");
EXPECT_EQ(std::string(buf), result);
}
TEST(strprintf, longstr) {
std::string aaaa(2000, 'a');
std::string bbbb(2000, 'b');
std::string x;
x = aaaa;
ASSERT_EQ(x, strprintf("%s", x.c_str()));
x += bbbb+x;
ASSERT_EQ(x, strprintf("%s", x.c_str()));
x += bbbb+x;
ASSERT_EQ(x, strprintf("%s", x.c_str()));
x += bbbb+x;
ASSERT_EQ(x, strprintf("%s", x.c_str()));
x += bbbb+x;
ASSERT_EQ(x, strprintf("%s", x.c_str()));
}
TEST(strprintf, wrappers) {
// can we print strings and smart-pointers directly?
char buf[200];
auto uptr = std::unique_ptr<int>{new int(17)};
sprintf(buf, "uptr %p", uptr.get());
EXPECT_EQ(std::string(buf), strprintf("uptr %p", uptr));
auto sptr = std::shared_ptr<double>{new double(19.)};
sprintf(buf, "sptr %p", sptr.get());
EXPECT_EQ(std::string(buf), strprintf("sptr %p", sptr));
EXPECT_EQ(std::string("fish"), strprintf("fi%s", std::string("sh")));
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment