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

Add an API for dynamic construction of argument lists #1170

Closed
vitaut opened this issue May 21, 2019 · 13 comments
Closed

Add an API for dynamic construction of argument lists #1170

vitaut opened this issue May 21, 2019 · 13 comments

Comments

@vitaut
Copy link
Contributor

vitaut commented May 21, 2019

Currently the number and types (but not values) of formatting arguments has to be known at compile time. Consider adding an API to allow fully dynamic construction of argument lists to support use cases like the one mentioned in #1168:

I have a few use cases, but they all boil down to wanting to externalize the format of whatever needs to be produced from the C++ code. So, for example, a config file might have a format string in it and that format string wants to extract zero or more named values from some data dictionary in the program.

@vincentlaucsb
Copy link

Adding on to #1168, I was working on a resume generator and ran into a similar issue.

One of the features I was working on was the ability for the user to define HTML templates like below:

        <header>
          <h1 id="name">Vincent La</h1>
          <p>
            <span>{Address}</span>
            <span>{Email}</span>
            <span>{LinkedIn}</span>
            <span>{Website}</span>
          </p>
        </header>

The main problem is that I want to be able to define the templates without having to touch C++ code. That means the parameters have to been parsed from some sort of data file, so the formatting arguments have to be constructed at runtime. In Python, I would just build a dict of arguments and pass them into str.format() directly. I was disappointed I couldn't do that here, and ended up writing my own function to do the formatting.

With that being said, I really liked a lot of the other features this library provided. I'm willing to help implement this if you have good pointers on where to start.

@vitaut
Copy link
Contributor Author

vitaut commented Jun 11, 2019

I think the easiest solution is to implement something like dynamic_arg_store which is similar to format_arg_store but owns the arguments and allows adding them dynamically. It can provide a conversion operator to basic_format_args that will allow passing it to vformat and other formatting functions once it's populated.

@joyqat
Copy link

joyqat commented Jul 2, 2019

Qt has a format API which looks like QString("%1, %2!").arg("hello").arg("world"), this operation will look and replace the placeholder %1 and %2 inside str.
Seems not so difficult to realize?
But in this way performance may not be as good.

@abrownsword
Copy link

Adding dynamic_arg_store as described would be extremely useful. My typical use case is that I have a std::map<std::string, my_type> and I want to add one or more of these to the dynamic_arg_store and then pass this to vformat. Ideally I could keep the dynamic_arg_store around and update it in place, and also used the new compiled format object with it to speed up the operation.

@slonm
Copy link

slonm commented Sep 23, 2019

While dynamic_arg_store is not offered i'm using currying based on kari.hpp :

#include <fmt/format.h>
#include <https://raw.githubusercontent.com/BlackMATov/kari.hpp/master/headers/kari.hpp/kari.hpp>
namespace fmt {

	namespace detail {

		template<class C>
		struct FormatWrapper {
			C c;

			FormatWrapper(const C& c)
			: c(c) {
			}

			std::string operator()() const {
				return c();
			}

			template <typename... As >
			auto operator()(As&&... as) const {
				auto c2 = c(std::forward<As>(as)...);
				return FormatWrapper < std::decay_t<decltype(c2)>>(std::move(c2));
			}

			operator std::string() const {
				return c();
			}
		};
	}

	inline auto Format(fmt::string_view format_str) {
		auto c = kari::curryV([format_str](auto&&... args) {
			return fmt::format(format_str, std::forward<decltype(args)>(args)...);
		});
		return fmt::detail::FormatWrapper<decltype(c)>(c);
	}
}

void main() {
    auto f = fmt::Format("{} {} {}");
    auto f1 = f("foo");
    auto f2 = f1("bar")("baz");
    std::string s = f2;
    fmt::print("{}\n", s);
}

@abrownsword
Copy link

While dynamic_arg_store is not offered i'm using currying based on [kari.hpp]

Does this support runtime determine number of arguments? i.e. I have my zero or more named args in a runtime constructed map and I want to pass them all to the Format function.

@slonm
Copy link

slonm commented Sep 27, 2019

Does this support runtime determine number of arguments? i.e. I have my zero or more named args in a runtime constructed map and I want to pass them all to the Format function.

True. Definetely no. It's only compile time wrapper for compile time variadic args function fmt::format()

@abrownsword
Copy link

Is this feature still on the radar?

@vitaut
Copy link
Contributor Author

vitaut commented Jan 29, 2020

It is but AFAIK noone is working on it at the moment.

@vsolontsov-ll
Copy link
Contributor

vsolontsov-ll commented Feb 29, 2020

Hm, I'm wondering if there's something wrong with just making internal::make_arg<>() public (I mean considering it as a part of interface and not as implementation detail) or providing a public wrapper around it?

There's even a unit test with dynamic arguments: TEST(FormatTest, Dynamic)

UPD: I realize lifetime for strings and custom objects is an issue but in may cases it's acceptable. E.g. in my case there's a binary dump and a format string to format it, char sequence is already stored in the dump, so its lifetime is guaranteed.

@vitaut
Copy link
Contributor Author

vitaut commented Mar 1, 2020

Lifetime is an issue. Even though it may be acceptable in some cases, making it public would create a big footgun.

@vsolontsov-ll
Copy link
Contributor

Thank you for your answer. I will give a try to add dynamic_arg_store.

As a side note I would say, it's debatable to some extent. E.g. STL doesn't prevent one from creating a container with pointers and either the programmer takes care of lifetime or UB happens.

@vitaut
Copy link
Contributor Author

vitaut commented Mar 16, 2020

This has been implemented in #1584. Thanks to @vsolontsov-ll!

@vitaut vitaut closed this as completed Mar 16, 2020
vitaut pushed a commit that referenced this issue May 9, 2020
Dynamic arguments storage. Implementation of enhancement from issue
#1170.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants