Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to format sequentially #21

Open
gabyx opened this issue Oct 29, 2014 · 9 comments
Open

Ability to format sequentially #21

gabyx opened this issue Oct 29, 2014 · 9 comments

Comments

@gabyx
Copy link

gabyx commented Oct 29, 2014

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 :-)

@c42f
Copy link
Owner

c42f commented Oct 30, 2014

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;
}

@gabyx
Copy link
Author

gabyx commented Oct 30, 2014

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
void add(const T&& value)

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!

@c42f
Copy link
Owner

c42f commented Oct 30, 2014

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.

@gabyx
Copy link
Author

gabyx commented Oct 30, 2014

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,

@c42f
Copy link
Owner

c42f commented Oct 31, 2014

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();

@gabyx
Copy link
Author

gabyx commented Oct 31, 2014

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!

@c42f
Copy link
Owner

c42f commented Oct 31, 2014

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.

@alfishe
Copy link

alfishe commented Dec 28, 2017

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

@c42f
Copy link
Owner

c42f commented Dec 30, 2017

@alfishe did you see #45 ? I'm going to merge that as soon as I've got time to properly look at it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants