Boost logo

Boost :

Subject: Re: [boost] Static constexpr map (was: Re: [gsoc16] Can I quickly check if the below really is the best approach?)
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2016-01-15 09:18:34


On 14 Jan 2016 at 13:02, Gottlob Frege wrote:

> I've experimented with static_map quite a bit. But never with
> C++11/14, unfortunately.
>
> Some thoughts:
>
> - once you have static_map<A,B>, the next thing you immediately want
> is bi_map<A,B> - to offer two way lookup.
> - and from there, you want multiple keys and columns: <A,B,C>
> - and maybe projection functions as well: <A,B,C, key(B)>

Added to the project idea at
https://svn.boost.org/trac/boost/wiki/SoC2016, thank you.

> I'm not sure, however, what the usage looks like for the bimap and
> multimap cases. It is no longer the simple syntax of mapvar[a] = b;

Easy if type Key != type Value OR if type Key == type Value and key
!= value, but implicit in a bimap is you can't modify anything at
all, the map is 100% const. That still has wide use cases.

> Possibly due to limitations with pre-11 C++, I had a two-step process
> for declaring/defining the map:
>
> // the map is this type
> typedef static_map<A,B> ab_map;
>
> // but the data/table is an array of this type:
> // using the inner element_type means you don't repeat A,B (more
> robust to changes)
> static ab_map::table_entry_type ab_table[] =
> {
> { a1, b1 },
> { a2, b2 },
> ...
> };
>
> // the actual map:
>
> ab_map theMap = ab_table; // constructor does magic
>
> Doing a bi-map is fairly easy above, as table_entry_type can actually
> be { A, B, reverse_index } but the { a1, b1 } initialization is still
> valid as the reverse_index gets initialized to 0. The reverse_index is
> basically an extra hidden column.
>
> Constexpr probably gets rid of many of those problems.

Very interesting, especially as you've tapped exactly where I got
this GSoC project idea from.

In the post peer review AFIO v2 rewrite I have this new concept of
"storage profiles" which is an empirically determined set of
characteristics about file i/o on some given combination of filing
system, disk drive, disk controller, motherboard, CPU and OS version.
Each storage profile is a static map of const char *keys to some type
T which can be any of std::string, unsigned long long, unsigned int,
float etc. and can be written or loaded from a YAML database. The
concept is that a "standard" YAML database can be kept in the AFIO
distro for quick lookup of common system configurations, but for
uncommon configurations a suite of tests can be run to figure out the
profile for some given storage combo.

This lets AFIO v2 become less conservative on some systems. For
example, if a system guarantees atomic updates for 4Kb write bursts
if and only if on a 4Kb aligned boundary (this is very common on PCIe
systems) but otherwise only guarantees atomic updates to a 64 byte
burst on a 64 byte aligned boundary, AFIO can dispense with explicit
byte range locking in a much wider range of use cases.

Anyway, AFIO v2's storage_profile looks a bit like this:

  //! A (possibly incomplet) profile of storage
  struct BOOST_AFIO_DECL storage_profile
  {
    ...
    item<unsigned long long> mem_quantity = { "system:mem:quantity",
&system::mem };
    item<float> mem_in_use = { "system:mem:in_use", &system::mem };

    // Controller characteristics
    item<std::string> controller_type = { "storage:controller:kind",
&storage::device };
    item<unsigned> controller_max_transfer = {
"storage:controller:max_transfer",
      storage::device,
      "The maximum number of bytes the disk controller can transfer
at once"
    };
    item<unsigned> controller_max_buffers = {
"storage:controller:max_buffers",
      storage::device,
      "The maximum number of scatter-gather buffers the disk
controller can handle"
    };
    ...
};

So what we are doing is to use in-class initialisation to default
construct all these member variables to their YAML section plus key,
an address of a function capable of figuring out that value through
testing, and an optional description text. This syntax makes it dead
easy to add new items to the profile, simply add another in-class
initialised member variable and voila it's done.

Now as much as the above is great, I am still not happy with this
design of storage_profile because I have to duplicate the YAML text
key and the variable name which seems unnecessary in C++ 14. I would
much prefer a static map of text keys to type erased value instead
where

storage_profile["storage:controller:max_buffers"].set_value(5);

... does *exactly* the right thing because the compiler, entirely at
compile time, turns the string key lookup into a storage location of
an unsigned value in storage_profile, so I would expect the assembler
generated by the compiler by the above statement to look something
like:

mov r0, #5
str r0, [storage_profile + #offset ]

... and that is entirely it. A proposed constexpr static map would
deliver exactly what I am looking for, and I for one would be
pleased.

tl;dr; I think we are on exactly the same page with the same use case
and problem to be solved here. Unsurprising seeing as we used to work
in the same team :)

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ 
http://ie.linkedin.com/in/nialldouglas/



Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk