lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi] Code review request


From: Greg Chicares
Subject: [lmi] Code review request
Date: Tue, 22 Sep 2020 11:26:35 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.11.0

Vadim--We have this pattern, for data used in reports:
 - data vary by context (e.g., by input fields such as 'ProductName')
 - store all context-specific values in some data structure somewhere
 - look them up in context, to generate reports
Often we store all potential values in a std::map--e.g.,
 - title_map_t static_titles() in 'ledger_evaluator.cpp'
and look them up by enumerators--e.g.,
 - finra_assumption_detail's anonymous local enum in 'pdf_command_wx.cpp'
just to cite a couple of examples in code you've worked on. Elsewhere:
 - 'dbnames.hpp' specifies an enum for the lookup key
 - 'dbdict.cpp' specifies product-specific values for each key, e.g.,
   in sample::sample(), and they're stored in product-specific XML files
 - 'ihs_basicval.cpp' reads those files and selects values in context, e.g.:
     database().query_into(DB_MaturityAge   , EndtAge);
Now we have a similar need, and before making any final decision about
a design to meet it, I'd like to ask whether this "map-lookup-by-enum"
style seems appropriate...or is there some better alternative?

This "similar need" is exemplified in branch odd/string_db, which I've
pushed to savannah. It's a single-commit throwaway branch for discussion
only: an actual working implementation for a single lookup, described
in 'ledger_invariant_init.cpp'.

We already have '.database' files that hold arithmetic scalars that can
vary by up to seven parameters ("axes") for each product, and '.policy'
files that hold strings--which today vary by product only, but need to
vary by the same parameters '.database' files allow. In several cases
we've used expedients like this:

    auto const smoke_or_tobacco = 
b->database().query<oenum_smoking_or_tobacco>(DB_SmokeOrTobacco);
    if(oe_tobacco_nontobacco == smoke_or_tobacco)
        {
        switch(b->yare_input_.Smoking)
            {
            case mce_smoker:    Smoker =    "Tobacco"; break;
            case mce_nonsmoker: Smoker = "Nontobacco"; break;
            case mce_unismoke:  Smoker = "Unitobacco"; break;
            }
        }
    else if(oe_smoker_nonsmoker == smoke_or_tobacco)
    ...

where a string must vary by a '.database' axis, but '.database' files
cannot hold strings. Perhaps worse, in 'ledger_invariant_init.cpp', a
'PolicyForm' string is specified in each '.policy' file, but sometimes
it must vary by the "state" axis, so we have this ad-hockery:

        bool alt_form = b->database().query<bool>(DB_UsePolicyFormAlt);
        PolicyForm = p.datum(alt_form ? "PolicyFormAlternative" : "PolicyForm");

which allows one default 'PolicyForm' and exactly one alternative.

I propose to rationalize those examples by introducing another level
of indirection, across the whole board:

 - move most strings out of '.policy' into a new file for product-specific lingo
 - look up those strings by new '.database' keys, which can vary by multiple 
axes

Thus, in the odd/string_db branch's microscopic example:

  // This can reside in a new file similar to 'dbnames.hpp':
    enum e_xyzzy
        {e_policy_form
        ,e_policy_form_KS_KY
        };
  // This can be serialized to and from a new '.lingo' file that holds all such
  // proprietary lingo for all products:
    static std::map<e_xyzzy,std::string> xyzzy
        {{e_policy_form, "UL32768-NY"}
        ,{e_policy_form_KS_KY, "UL32768-X"}
        };
  // A cover function might unify this two-stage lookup:
    auto policy_form = b->database().query<e_xyzzy>(DB_L_PolicyForm);
    PolicyForm = xyzzy[policy_form];

Do you see some better way of doing this? Here are some considerations:
 - The '.lingo' file would contain about 300 entries initially, and
   would probably never grow past about 1000. Entries might range from
   five to perhaps five thousand characters, a typical size being 100.
 - It has to be an external file, because it contains some proprietary
   lingo that must be sequestered so that lmi remains free.
 - lmi can read it from file OAOO, at startup; at run time, there will
   be no insertions or deletions.
 - Thus, it should probably be a std::unordered_map.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]