Open gabyx opened 10 years ago
Can you give some real world code where you need to do this? I'd like to better understand exactly what you're trying to do (for example, do you want this because you've got a conditional branch when adding arguments? Is it because you want to pass op
through to a function? Is it some other reason?).
The tfm::vformat()
machinery basically lets you do this already. For example, if you define the class VFormatList as below, you can pass it through to tfm::vformat
.
#include <tinyformat.h>
#include <vector>
#include <memory>
class VFormatList
{
public:
// Attempt to avoid copying and storing the value where possible.
#if 0
template<typename T>
void add(const T& value)
{
m_argList.emplace_back(value);
}
template<typename T>
void add(const T&& value)
{
m_argStore.emplace_back(new AnyT<T>(std::move(value)) );
const T& storedValue = static_cast<AnyT<T>&>(*m_argStore.back()).value;
m_argList.emplace_back(storedValue);
}
#endif
// Edit - actually the above will break for the user in many weird and wonderful ways.
// It's probably just a bad idea! The following should work, though it's quite inefficient.
template<typename T>
void add(const T& value)
{
m_argStore.push_back(std::unique_ptr<AnyT<T>>(new AnyT<T>(value)));
const T& storedValue = static_cast<AnyT<T>&>(*m_argStore.back()).value;
m_argList.emplace_back(storedValue);
}
operator tfm::FormatList()
{
return tfm::FormatList(m_argList.data(), m_argList.size());
}
private:
struct Any { };
template<typename T>
struct AnyT : Any
{
T value;
AnyT(const T& value) : value(value) { }
};
std::vector<tfm::detail::FormatArg> m_argList;
std::vector<std::unique_ptr<Any>> m_argStore;
};
int main()
{
VFormatList args;
std::string str = "asdf";
args.add(str);
args.add(42.42);
tfm::vformat(std::cout, "%s : %.1f\n", args);
return 0;
}
Thanks for the help, I have exactly the mentioned case when I want to add arguments in a loop at runtime. Thanks for adding this example, is that already explained in the doku?
By the way:
template
is an universal reference (it could be also a lvalue reference, but then the first overload would be taken right?)
BR Gabriel
p.S: You are also the one who maintains Aqsis ? :-) . I am the same guy who asked about the Sphere and RIB files :-) last weeks :-) Thanks for the help!
The reason I'm asking for a "real world" case, is that I'd write your example as:
tfm::format("%i , %i ", a, b);
and I'm not sure how having an incremental interface could make that shorter or clearer.
Regarding loops, a format string is already basically limited to a fixed number of arguments, so I don't see how it's natural to use one in a loop. If you have some array a
and you want to format in a loop, why not do something like:
double a[10] = {/* some example array */};
std::ostringstream out;
for (int i = 0; i < 10; ++i)
tfm::format(out, "%.6f ", a[i]);
std::string s = out.str();
The reason I played the trick with the rvalue reference add(const T&& value)
is that you definitely need to copy any rvalue (placing in m_argStore
) so that the temporary object isn't deallocated before the call to vformat
. On the other hand, I was trying to avoid the copy if it's an lvalue, especially since some types don't even have a copy constructor. In hindsight this is going to break for the user in weird and wonderful ways, especially for local variables inside a loop.
About aqsis - yes that's me, though I don't really count as a maintainer anymore due to being too time poor. I still read the mailing list, happy to provide the occasional bit of help.
Ok, maybe you need some more information:
I use tinyformat for an StringFormattingNode
excution node.
this execution node has input sockets and output sockets
the ouput socket is a std::string
the format string and the inputs sockets can be specified with an XML, so the XML is read at runtime and the StringFormattingNode
is created with the inputs sockets specified, e.g. one double
, one int
, one std::string
and a format string "%d : %i : %s"
.
In this context, when executing the node, it retrieves all inputs and outputs the formatted string
So this is done in a loop with m_vformatList.add( input[i]->getValue() )
and then the list is converted with the help of your example :-). I cannot use the variadic argument tfm::format() function because the arguments can only be accessed in a loop.
thats the whole context,
Ok thanks, that explanation makes a lot more sense. I think it's a relatively unusual use case, but certainly a valid thing to want to do. Can you guarantee that the format arguments have a lifetime outside the scope of your loop? In that case you can remove the Any
wrapper class stuff I have above and it should be pretty efficient.
It's also technically possible to have a format iterator which would sort out the issues with format argument lifetime in a more elegant way. In fact, the internals in tinyformat version 1.3 had a class detail::FormatIterator
. This wasn't part of the public API and I've since removed it in the name of implementation simplicity, but usage would have looked something like
std::ostringstream out;
tfm::detail::FormatIterator fmt(out, "%i , %i ");
fmt.accept(a);
fmt.accept(b);
fmt.finish();
std::string s = out.str();
Ahh jeah that would be cool as well :-) I dont think this will overbloat the simplicity as it already is :-), could you still include that part of functionality in an additional include header?
thanks a lot :-)
by the way, a really cool library!
I can't put back the FormatIterator
without some significant internal work, at least not in a general way which would avoid the inelegant use of something like to Any
as in VFormatList
above.
I'm open to the idea of having a separate header or headers containing useful examples which people can modify for their own needs.
Probably it's simpler to explain use-case by example in C# where this paradigm is used extensively. https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx
Benefits - you can reuse indexed placeholders and bound values multiple times without duplication in parameters. But it's not printf-like style anymore and probably not "tiny"format model. It's like creating new "power"format component
@alfishe did you see #45 ? I'm going to merge that as soon as I've got time to properly look at it again.
Would it be possible to have the following feature:
auto op = tfm::formatOp("%i , %i "); op % a; op % b; op % c ; // this would result in an assert or exception
Thanks for considering this :-), this makes sequential parsing in values and converting them into a string extremely easy :-)