lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] odd/string_db3 9250a58: Allow product lingo to vary


From: Greg Chicares
Subject: [lmi-commits] [lmi] odd/string_db3 9250a58: Allow product lingo to vary by state, gender, and so on
Date: Fri, 13 Nov 2020 05:34:07 -0500 (EST)

branch: odd/string_db3
commit 9250a58b76150804c4aeaa4f5295eafd155c884e
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Allow product lingo to vary by state, gender, and so on
    
    This replaces commit bba966795d on branch 'odd/string_db2'. It is
    presented as a single-commit new branch for convenient reference.
    The most important change is writing strings to a '.lingo' file,
    reading them back into an object of class lingo, and using that
    object to look them up.
    
    See the commentary and simple example in 'ledger_invariant_init.cpp'.
---
 antediluvian_stubs.cpp    |   6 ++
 basic_values.hpp          |   3 +
 dbdict.cpp                |   9 ++
 dbdict.hpp                |   1 +
 dbnames.hpp               |   4 +
 dbnames.xpp               |   2 +
 ihs_basicval.cpp          |   7 +-
 ledger_invariant_init.cpp | 102 ++++++++++++++++++++
 lingo.cpp                 | 232 ++++++++++++++++++++++++++++++++++++++++++++++
 lingo.hpp                 |  12 +++
 sample.hpp                | 132 +++++++++++++++++++++++++-
 11 files changed, 508 insertions(+), 2 deletions(-)

diff --git a/antediluvian_stubs.cpp b/antediluvian_stubs.cpp
index c3123a8..236d0c1 100644
--- a/antediluvian_stubs.cpp
+++ b/antediluvian_stubs.cpp
@@ -23,6 +23,7 @@
 
 #include "fund_data.hpp"
 #include "gpt_server.hpp"
+#include "lingo.hpp"
 #include "lmi.hpp"                      // is_antediluvian_fork()
 #include "mec_server.hpp"
 #include "product_data.hpp"
@@ -88,6 +89,11 @@ bool gpt_state::is_detritus(std::string const&) const
     return false;
 }
 
+std::string const& lingo::lookup(int) const
+{
+    return empty_string;
+}
+
 mec_server::mec_server(mcenum_emission)
 {}
 
diff --git a/basic_values.hpp b/basic_values.hpp
index ce3816f..9d4dcaf 100644
--- a/basic_values.hpp
+++ b/basic_values.hpp
@@ -59,6 +59,7 @@ class Irc7702A;
 class Loads;
 class MortalityRates;
 class death_benefits;
+class lingo;
 class modal_outlay;
 class premium_tax;
 class rounding_rules;
@@ -119,6 +120,7 @@ class LMI_SO BasicValues
     yare_input                          yare_input_;
     product_data     const              product_;
     product_database const              database_;
+    std::shared_ptr<lingo>              lingo_;
     std::shared_ptr<FundData>           FundData_;
     std::shared_ptr<rounding_rules>     RoundingRules_;
     std::shared_ptr<stratified_charges> StratifiedCharges_;
@@ -133,6 +135,7 @@ class LMI_SO BasicValues
 
     product_data     const& product () const {return product_;}
     product_database const& database() const {return database_;}
+////lingo            const& lingo   () const {return *lingo_;}
 
     double GetAnnualTgtPrem(int a_year, double a_specamt) const;
 
diff --git a/dbdict.cpp b/dbdict.cpp
index e8b0a5b..513fa9e 100644
--- a/dbdict.cpp
+++ b/dbdict.cpp
@@ -38,6 +38,7 @@
 #include "my_proem.hpp"                 // ::write_proem()
 #include "oecumenic_enumerations.hpp"
 #include "premium_tax.hpp"              // 
premium_tax_rates_for_life_insurance()
+#include "sample.hpp"                   // superior::lingo
 #include "xml_lmi.hpp"
 #include "xml_serialize.hpp"
 
@@ -419,6 +420,7 @@ void DBDictionary::ascribe_members()
     ascribe("PartialMortTable"    , &DBDictionary::PartialMortTable    );
     ascribe("UsePolicyFormAlt"    , &DBDictionary::UsePolicyFormAlt    );
     ascribe("AllowGroupQuote"     , &DBDictionary::AllowGroupQuote     );
+    ascribe("L_PolicyForm"        , &DBDictionary::L_PolicyForm        );
     ascribe("WeightClass"         , &DBDictionary::WeightClass         );
     ascribe("WeightGender"        , &DBDictionary::WeightGender        );
     ascribe("WeightSmoking"       , &DBDictionary::WeightSmoking       );
@@ -926,7 +928,14 @@ sample::sample()
     alt_form[mce_s_KS] = true;
     alt_form[mce_s_KY] = true;
     Add({DB_UsePolicyFormAlt, premium_tax_dimensions, alt_form});
+
     Add({DB_AllowGroupQuote     , true});
+
+    // Use alternative policy form name in states beginning with "K".
+    std::vector<double> pol_form(e_max_dim_state, superior::policy_form);
+    pol_form[mce_s_KS] = superior::policy_form_KS_KY;
+    pol_form[mce_s_KY] = superior::policy_form_KS_KY;
+    Add({DB_L_PolicyForm    , premium_tax_dimensions, pol_form});
 }
 
 sample2finra::sample2finra()
diff --git a/dbdict.hpp b/dbdict.hpp
index d0a78ef..3d4b8fb 100644
--- a/dbdict.hpp
+++ b/dbdict.hpp
@@ -403,6 +403,7 @@ class LMI_SO DBDictionary
     database_entity PartialMortTable    ;
     database_entity UsePolicyFormAlt    ;
     database_entity AllowGroupQuote     ;
+    database_entity L_PolicyForm        ;
     database_entity WeightClass         ;
     database_entity WeightGender        ;
     database_entity WeightSmoking       ;
diff --git a/dbnames.hpp b/dbnames.hpp
index 12bac7c..ab2694c 100644
--- a/dbnames.hpp
+++ b/dbnames.hpp
@@ -527,6 +527,10 @@ enum e_database_key
         ,DB_UsePolicyFormAlt
         ,DB_AllowGroupQuote
 
+    ,DB_Topic_Lingo
+
+        ,DB_L_PolicyForm
+
     ,DB_Topic_Weights
 
         ,DB_WeightClass
diff --git a/dbnames.xpp b/dbnames.xpp
index aa113ab..c5e5092 100644
--- a/dbnames.xpp
+++ b/dbnames.xpp
@@ -356,6 +356,8 @@
 {DB_PartialMortTable,DB_Topic_Miscellanea,"PartialMortTable","Partial 
mortality table (index in mortality table database)",}, \
 {DB_UsePolicyFormAlt,DB_Topic_Miscellanea,"UsePolicyFormAlt","Use alternative 
policy-form name: 0=no, 1=yes",}, \
 {DB_AllowGroupQuote,DB_Topic_Miscellanea,"AllowGroupQuote","Allow group 
premium quotes: 0=no, 1=yes",}, \
+{DB_Topic_Lingo,DB_FIRST,"Lingo","Substitutable text strings for report 
generation",}, \
+{DB_L_PolicyForm,DB_Topic_Lingo,"L_PolicyForm","Policy form: string index",}, \
 {DB_Topic_Weights,DB_FIRST,"Weights","Weights for profit analysis cells [not 
yet implemented]",}, \
 {DB_WeightClass,DB_Topic_Weights,"WeightClass","Weight by underwriting class 
[not yet implemented]",}, \
 {DB_WeightGender,DB_Topic_Weights,"WeightGender","Weight by gender [not yet 
implemented]",}, \
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index 21aa86e..b91fd52 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -41,6 +41,7 @@
 #include "ihs_irc7702a.hpp"
 #include "input.hpp"
 #include "interest_rates.hpp"
+#include "lingo.hpp"
 #include "loads.hpp"
 #include "math_functions.hpp"
 #include "mc_enum_types_aux.hpp"        // mc_str()
@@ -208,6 +209,7 @@ void BasicValues::Init()
             << LMI_FLUSH
             ;
         }
+    lingo_.reset(new lingo(AddDataDir(product().datum("LingoFilename"))));
     FundData_.reset(new FundData(AddDataDir(product().datum("FundFilename"))));
     RoundingRules_.reset
         (new rounding_rules(AddDataDir(product().datum("RoundingFilename")))
@@ -298,7 +300,10 @@ void BasicValues::GPTServerInit()
             std::string("Issue age greater than maximum")
             );
         }
-//  FundData_       = new FundData
+//  lingo_ = new lingo
+//      (AddDataDir(product().datum("LingoFilename"))
+//      );
+//  FundData_ = new FundData
 //      (AddDataDir(product().datum("FundFilename"))
 //      );
     RoundingRules_.reset
diff --git a/ledger_invariant_init.cpp b/ledger_invariant_init.cpp
index 7639d82..9e756d3 100644
--- a/ledger_invariant_init.cpp
+++ b/ledger_invariant_init.cpp
@@ -32,6 +32,7 @@
 #include "death_benefits.hpp"
 #include "fund_data.hpp"
 #include "interest_rates.hpp"
+#include "lingo.hpp"
 #include "lmi.hpp"                      // is_antediluvian_fork()
 #include "loads.hpp"
 #include "mc_enum_types_aux.hpp"        // mc_str()
@@ -324,7 +325,108 @@ void LedgerInvariant::Init(BasicValues const* b)
 
         // Strings.
 
+// The next couple hundred "p.datum" lines will be replaced.
+//
+// Old way: store strings in class product_data. That works, but
+// strings are one-dimensional--not like the (up to) seven-dimensional
+// class database_entity used by class DBDictionary. A database_entity
+// is a 0- to 7-dimensional matrix of type 'double' (which might hold
+// actual floating point values, or integral values).
+//
+// Alternative not considered: devise a "string_database_entity" that
+// can hold a 0- to 7-dimensional string; then radically redesign
+// class product_data to hold that new type instead of plain strings.
+// Too much labor. Too much duplication.
+//
+// Crucial insight: Actuarial tables are zero-dimensional per se, but
+// lmi treats them as multidimensional by storing their table numbers
+// in objects of class database_entity, e.g.:
+//
+//  // 1983 GAM; unisex=male because no unisex table was published.
+//  double T83Gam[3] = {825, 826, 826,}; // f, m, u
+//  Add({DB_PartialMortTable, e_number_of_axes, dims311, T83Gam});
+//
+// Something very similar can certainly be done for strings.
+//
+// New way: establish a compendium of company-specific strings, and
+// store indexes into that compendium in database_entity objects.
+// The only question is what form that compendium should take.
+// Answer: one XML '.lingo' file to be shared by a company's entire
+// portfolio of products. It is most convenient to associate each
+// string with an enumerator, to provide a descriptive name, but some
+// such names might be particular to the company, e.g.:
+//
+// static std::string const S_DefnCSV = // default for most products
+//   "Cash surrender value is account value less any surrender charge.";
+//
+// static std::string const S_DefnCSV_turbo =
+//   "Cash surrender value is account value less any surrender charge,"
+//   " plus the Turbo Asset Boost provided by a special endorsement.";
+//
+// ...where each product-file entity already has a name, and its
+// string values are named with an "S_" prefix, and if necessary a
+// suffix indicating specialization--so a specialized footnote for a
+// particular product might naturally be named with a "_turbo" suffix,
+// for which a different company would have no use. Today's product
+// files might have
+//  item("DefnAV")                     = S_DefnAV;
+// in the generic case, and
+//  item("DefnAV")                     = S_DefnCSV_turbo;
+// for "turbo" products. In (proprietary) code that creates that
+// company's product files, it's convenient to provide a "turbo"
+// enumerator:
+//  enum e_lingo
+//      {e_defn_av
+//      ,e_defn_av_turbo
+//      };
+//  static std::unordered_map<e_lingo,std::string> m
+//      {{e_defn_av, S_DefnAV}
+//      ,{e_defn_av_turbo, S_DefnCSV_turbo}
+//      };
+// and use it to replace '.policy'-generation code like this:
+//  item("DefnAV") = S_DefnCSV_turbo;
+// with '.database'-generation code like this:
+//  Add({DB_L_DefnAV, e_defn_av_turbo});
+//
+// The proprietary "turbo" feature doesn't leak into lmi's public
+// code, because a proprietary 'e_lingo' is used only in proprietary
+// code that writes an XML file with integer rather than enumerative
+// keys, which are read into a map<int,std::string> in public code.
+//
+// What about strong typing? That's present on the write side, where
+// it can be used to ensure that no invalid "lingo" file is written.
+// Therefore, it's not very important on the read side, where it could
+// only verify the validity of a file already known to be valid. It's
+// not really needed on either side: actuarial-table numbers are naked
+// integers, yet they have worked just fine for decades.
+//
+// Here's a tiny example that refactors policy-form strings.
+// Today, 'sample' products have this in 'product_data.cpp':
+//  item("PolicyForm")                 = glossed_string("UL32768-NY");
+//  item("PolicyFormAlternative")      = glossed_string("UL32768-X");
+// and this in 'dbdict.cpp':
+//  // Use alternative policy form name in states beginning with "K".
+//  std::vector<double> alt_form(e_max_dim_state);
+//  alt_form[mce_s_KS] = true;
+//  alt_form[mce_s_KY] = true;
+//  Add({DB_UsePolicyFormAlt, premium_tax_dimensions, alt_form});
+// That's the old way:
+//   - '.policy': contains every unique string (two in this case)
+//   - '.database': indicates which of those strings to choose
+
         PolicyForm = p.datum(alt_form ? "PolicyFormAlternative" : 
"PolicyForm");
+
+// Here's the new way, which can replace the "p.datum" line above,
+// as well as the assignment of local 'alt_form' above. (For now,
+// it's implemented only for 'sample' products.)
+
+if("sample" == ProductName)
+    {
+        auto policy_form = b->database().query<int>(DB_L_PolicyForm);
+        std::string PolicyForm_NEW = b->lingo_->lookup(policy_form);
+// Either way, the result is the same:
+LMI_ASSERT(PolicyForm_NEW == PolicyForm);
+    }
         PolicyMktgName             = p.datum("PolicyMktgName"                 
);
         PolicyLegalName            = p.datum("PolicyLegalName"                
);
         CsoEra     = mc_str(b->database().query<mcenum_cso_era>(DB_CsoEra));
diff --git a/lingo.cpp b/lingo.cpp
index 00dc4ab..667ee7d 100644
--- a/lingo.cpp
+++ b/lingo.cpp
@@ -25,14 +25,27 @@
 
 #include "alert.hpp"
 #include "data_directory.hpp"           // AddDataDir()
+#include "map_lookup.hpp"
 #include "my_proem.hpp"                 // ::write_proem()
+#include "sample.hpp"                   // superior::lingo
+//#include "timer.hpp"
 #include "xml_lmi.hpp"
+#include "xml_serialize.hpp"
 
 #include <boost/filesystem/convenience.hpp>
 #include <boost/filesystem/path.hpp>
 
+/// Private default ctor for friend class and write_lingo_files().
+#if 0
+lingo::lingo()
+{
+}
+#endif // 0
+/// Construct from filename.
+
 lingo::lingo(std::string const& filename)
 {
+//    Timer timer;
     xml_lmi::dom_parser parser(filename);
     xml::element const& root = parser.root_node(xml_root_name());
     int file_version = 0;
@@ -45,15 +58,234 @@ lingo::lingo(std::string const& filename)
             << LMI_FLUSH
             ;
         }
+    xml_serialize::from_xml(root, map_);
+//    warning() << "Load lingo: " << timer.stop().elapsed_msec_str() << 
std::flush;
+}
+
+std::string const& lingo::lookup(int index) const
+{
+    return map_lookup(map_, index);
 }
 
 void lingo::write_lingo_files()
 {
+        // delete...
+static std::string const S_FnMonthlyDeductions =
+  "Monthly charges are deducted from the account value; if it is depleted,"
+  " additional premiums may be required.";
+
+// These two certifications are copied verbatim et literatim from the
+// illustration reg.
+static std::string const S_IllRegCertAgent =
+  "I certify that this illustration has been presented to the applicant and"
+  " that I have explained that any non-guaranteed elements illustrated are"
+  " subject to change. I have made no statements that are inconsistent with"
+  " the illustration.";
+static std::string const S_IllRegCertClient =
+  "I have received a copy of this illustration and understand that any"
+  " non-guaranteed elements illustrated are subject to change and could be"
+  " either higher or lower. The agent has told me they are not guaranteed.";
+
+static std::string const S_FnMaturityAge =
+  "¶¶Maturity age: {{EndtAge}}.";
+
+static std::string const S_FnPartialMortality =
+  "¶¶Columns reflect mortality, beginning at {{PartMortTableMult[0]}}"
+  " of the {{PartMortTableName}} table,"
+  " with all deaths at the end of each year"
+  "{{#SurviveToExpectancy}}"
+  " and survival limited to life expectancy"
+  "{{/SurviveToExpectancy}}"
+  "{{#SurviveToYear}}"
+  " and survival limited to {{SurvivalMaxYear}} years"
+  "{{/SurviveToYear}}"
+  "{{#SurviveToAge}}"
+  " and survival limited to age {{SurvivalMaxAge}}"
+  "{{/SurviveToAge}}"
+  ".";
+
+static std::string const S_FnProspectus =
+  "Must be preceded or accompanied by a prospectus.";
+static std::string const S_FnInitialSpecAmt =
+  "The initial specified amount is ${{InitTotalSA}}.";
+static std::string const S_FnInforceAcctVal =
+  "The inforce account value is ${{InforceTotalAV}}.";
+static std::string const S_FnInforceTaxBasis =
+  "The inforce tax basis is ${{InforceTaxBasis}}.";
+static std::string const S_Fn1035Charge =
+  "A charge may be deducted from the proceeds of a 1035 exchange.";
+static std::string const S_FnMecExtraWarning =
+  "{{#IsMec}}¶¶This is a Modified Endowment Contract.{{/IsMec}}";
+static std::string const S_FnNotTaxAdvice =
+  "{{InsCoShortName}} cannot give tax advice. Consult your own advisors.";
+static std::string const S_FnImf =
+  "Initial investment management fee: {{TotalIMF[0]}}.";
+static std::string const S_FnCensus =
+  ""; // There is no census attached to a composite.
+static std::string const S_FnDacTax =
+  "There is no explicit charge for DAC tax.";
+
+static std::string const S_FnDefnLifeIns =
+  "This policy is intended to qualify as life insurance under the IRC §7702"
+  "{{#DefnLifeInsIsGPT}}"
+  " guideline premium test. ${{InitGSP}} is the guideline single premium,"
+  " and ${{InitGLP}} is the guideline level premium."
+  "{{/DefnLifeInsIsGPT}}"
+  "{{^DefnLifeInsIsGPT}}"
+  "cash value accumulation test."
+  "{{/DefnLifeInsIsGPT}}";
+
+static std::string const S_FnBoyEoy =
+  "Premiums are payable in advance. Benefits are as of year end.";
+static std::string const S_FnGeneralAccount =
+  "The general account credits interest of at least 
{{InitAnnGenAcctInt_Guaranteed}}.";
+static std::string const S_FnPpMemorandum =
+  "Must be preceded or accompanied by a prospectus.";
+static std::string const S_FnPpAccreditedInvestor =
+  "Available only to accredited investors.";
+static std::string const S_FnPpLoads =
+  ""; // Explanation of any special loads.
+static std::string const S_FnProposalUnderwriting =
+  ""; // Explanation of group underwriting.
+static std::string const S_FnGuaranteedPremium =
+  "An outlay of ${{GuarPrem}} ({{InitEeMode}}) will guarantee coverage"
+  " to age {{EndtAge}}"
+  "{{#DefnLifeInsIsGPT}}"
+  ", subject to guideline premium test limits"
+  "{{/DefnLifeInsIsGPT}}"
+  ".";
+static std::string const S_FnOmnibusDisclaimer =
+  "Non-guaranteed values are based on current assumptions, which are"
+  " subject to change. Actual results may be more or less favorable.";
+static std::string const S_FnInitialDbo =
+  "The initial death benefit option is {{InitDBOpt}}.";
+static std::string const S_DefnGuarGenAcctRate =
+  "¶¶«Guaranteed Crediting Rate:»"
+  " The minimum annual interest rate credited on unloaned funds."
+  ;
+static std::string const S_DefnAV =
+  "Account value is the accumulation of payments less charges and 
disbursements.";
+static std::string const S_DefnCSV =
+  "Cash surrender value is account value less any surrender charge.";
+static std::string const S_DefnMec =
+  "A Modified Endowment Contract is a contract that does not qualify"
+  " for favorable tax treatment under IRC §7702A.";
+static std::string const S_DefnOutlay =
+  "Outlay is premium paid out of pocket.";
+static std::string const S_DefnSpecAmt =
+  "Specified amount is the nominal face amount.";
+        // ...delete
+    // superior::lingo enumerators are used for clarity in specifying
+    // this map. They decay to integers in the resulting file, which
+    // can therefore be read without the enumerators being visible.
+    static std::unordered_map<superior::lingo,std::string> enumerated_map
+        {{superior::policy_form      , "UL32768-NY"}
+        ,{superior::policy_form_KS_KY, "UL32768-X"}
+        // delete...
+        ,{superior::e100, "UL, Supreme"}
+        ,{superior::e101, "Flexible Premium Adjustable Life Insurance Policy"}
+        ,{superior::e102, "Superior Life"}
+        ,{superior::e103, "Superior Life Insurance Company"}
+        ,{superior::e104, "Superior, WI 12345"}
+        ,{superior::e105, "246 Main, Street"}
+        ,{superior::e106, "(800) 555-1212"}
+        ,{superior::e107, "Superior, Securities"}
+        ,{superior::e108, "246-M Main, Street, Superior, WI 12345"}
+        ,{superior::e109, "Superior Investors"}
+        ,{superior::e110, "246-C Main, Street, Superior, WI 12345"}
+        ,{superior::e111, "Account"}
+        ,{superior::e112, "Cash, Surrender"}
+        ,{superior::e113, "Cash, Surr"}
+        ,{superior::e114, "No-lapse Provision"}
+        ,{superior::e115, "contract"}
+        ,{superior::e116, "Death Benefit Option"}
+        ,{superior::e117, "A"}
+        ,{superior::e118, "B"}
+        ,{superior::e119, "ROP"}
+        ,{superior::e120, "MDB"}
+        ,{superior::e121, "General Account"}
+        ,{superior::e122, "General Account (GA)"}
+        ,{superior::e123, "Separate Account"}
+        ,{superior::e124, "Specified Amount"}
+        ,{superior::e125, "Specified (Face) Amount"}
+        ,{superior::e126, "Medical"}
+        ,{superior::e127, "Paramedical"}
+        ,{superior::e128, "Nonmedical"}
+        ,{superior::e129, "Simplified Issue"}
+        ,{superior::e130, "Guaranteed Issue"}
+        ,{superior::e131, "Preferred"}
+        ,{superior::e132, "Standard"}
+        ,{superior::e133, "Rated"}
+        ,{superior::e134, "Ultrapreferred"}
+        ,{superior::e135, S_FnMonthlyDeductions}
+        ,{superior::e136, S_IllRegCertAgent}
+        ,{superior::e137, S_IllRegCertAgent}
+        ,{superior::e138, S_IllRegCertAgent}
+        ,{superior::e139, S_IllRegCertClient}
+        ,{superior::e140, S_IllRegCertClient}
+        ,{superior::e141, S_IllRegCertClient}
+        ,{superior::e142, S_FnMaturityAge}
+        ,{superior::e143, S_FnPartialMortality}
+        ,{superior::e144, S_FnProspectus}
+        ,{superior::e145, S_FnInitialSpecAmt}
+        ,{superior::e146, S_FnInforceAcctVal}
+        ,{superior::e147, S_FnInforceTaxBasis}
+        ,{superior::e148, S_Fn1035Charge}
+        ,{superior::e149, S_FnMecExtraWarning}
+        ,{superior::e150, S_FnNotTaxAdvice}
+        ,{superior::e151, ""}
+        ,{superior::e152, S_FnImf}
+        ,{superior::e153, S_FnCensus}
+        ,{superior::e154, S_FnDacTax}
+        ,{superior::e155, S_FnDefnLifeIns}
+        ,{superior::e156, S_FnBoyEoy}
+        ,{superior::e157, S_FnGeneralAccount}
+        ,{superior::e158, S_FnPpMemorandum}
+        ,{superior::e159, S_FnPpAccreditedInvestor}
+        ,{superior::e160, S_FnPpLoads}
+        ,{superior::e161, S_FnProposalUnderwriting}
+        ,{superior::e162, S_FnGuaranteedPremium}
+        ,{superior::e163, S_FnOmnibusDisclaimer}
+        ,{superior::e164, S_FnInitialDbo}
+        ,{superior::e165, S_DefnGuarGenAcctRate}
+        ,{superior::e166, S_DefnAV}
+        ,{superior::e167, S_DefnCSV}
+        ,{superior::e168, S_DefnMec}
+        ,{superior::e169, S_DefnOutlay}
+        ,{superior::e170, S_DefnSpecAmt}
+        ,{superior::e171, "Accident"}
+        ,{superior::e172, "Insurability"}
+        ,{superior::e173, "Child"}
+        ,{superior::e174, "Spouse"}
+        ,{superior::e175, "Term"}
+        ,{superior::e176, "Waiver"}
+        ,{superior::e177, "Acceleration"}
+        ,{superior::e178, "Overloan"}
+        ,{superior::e179, "Guaranteed mortality basis: {{CsoEra}} CSO."}
+        ,{superior::e180, "Policy form UL32768-NY is marketed as 'UL, 
Supreme'."}
+        ,{superior::e181, "UL, SUPREME®"}
+        ,{superior::e182, "This is not an offer of insurance."}
+        ,{superior::e183, "Available riders: accident and waiver."}
+        ,{superior::e184, "Policy form UL32768-NY is a flexible premium 
contract."}
+        ,{superior::e185, "Not available in all states."}
+        ,{superior::e186, "Read the prospectus carefully."}
+        ,{superior::e187, "Securities underwritten by, Superior Securities."}
+        ,{superior::e188, "Securities offered through, Superior Brokerage."}
+        ,{superior::e189, "Mandatory"}
+        ,{superior::e190, "Voluntary"}
+        ,{superior::e191, "Fusion"}
+        ,{superior::e192, "The employer pays all premiums."}
+        ,{superior::e193, "The employee pays all premiums."}
+        ,{superior::e194, "The employer and employee pay their respective 
premiums."}
+        // ...delete
+        };
+
     fs::path const path(AddDataDir("sample.lingo"));
     xml_lmi::xml_document document(xml_root_name());
     write_proem(document, fs::basename(path));
     xml::element& root = document.root_node();
     xml_lmi::set_attr(root, "version", class_version());
+    xml_serialize::to_xml(root, enumerated_map);
     document.save(path.string());
 }
 
diff --git a/lingo.hpp b/lingo.hpp
index 4d56fe6..3323d98 100644
--- a/lingo.hpp
+++ b/lingo.hpp
@@ -24,23 +24,33 @@
 
 #include "config.hpp"
 
+//#include "cache_file_reads.hpp"
 #include "so_attributes.hpp"
 #include "xml_lmi_fwd.hpp"
 
 #include <string>
+#include <unordered_map>
 
 /// Company-specific lingo.
 
 class LMI_SO lingo final
+//    :public cache_file_reads<lingo>
 {
   public:
     explicit lingo(std::string const& filename);
+//  ~lingo() = default; // TODO virtual?
+
+    std::string const& lookup(int) const;
 
     // Legacy functions to support creating product files programmatically.
     static void write_lingo_files();
     static void write_proprietary_lingo_files();
 
   private:
+//  lingo();
+//  lingo(lingo const&) = delete;
+//  lingo& operator=(lingo const&) = delete;
+
     // This class does not derive from xml_serializable, but it
     // implements these three functions that are akin to virtuals
     // of class xml_serializable.
@@ -50,6 +60,8 @@ class LMI_SO lingo final
         (xml_lmi::xml_document& document
         ,std::string const&     file_leaf_name
         );
+
+    std::unordered_map<int,std::string> map_;
 };
 
 #endif // lingo_hpp
diff --git a/sample.hpp b/sample.hpp
index 01024ae..742569e 100644
--- a/sample.hpp
+++ b/sample.hpp
@@ -24,6 +24,136 @@
 
 #include "config.hpp"
 
-// Coming soon...
+    // default enum:
+    //   implicitly converts to int
+    //   pollutes namespace
+    //   underlying type not specified
+    //
+    // #include <type_traits>
+    //
+    // template <typename E>
+    // constexpr auto to_underlying(E e) noexcept
+    // {
+    //     return static_cast<std::underlying_type_t<E>>(e);
+    // }
+    //
+    // but instead just define it in a namespace, with ": int"
+
+// For now, this file contains only an enumeration, but someday it may
+// include other information that applies to an entire portfolio.
+
+/// For the fictional Superior Life Insurance Company of Superior, WI.
+namespace superior
+{
+/// Enumerate lingo strings.
+///
+/// This is deliberately defined with enum-key 'enum' rather than
+/// 'enum class' or 'enum struct'. Because it is defined inside a
+/// namespace, with an enum-base, it is the same as an 'enum class'
+/// except that its enumerators decay to int as nature intended.
+///
+/// Use nondefault initializers just to demonstrate that they work.
+/// Ultimately, enumerator zero will be reserved for an empty string.
+
+enum lingo : int
+    {policy_form       = 13
+    ,policy_form_KS_KY = 0
+    ,e100 = 100
+    ,e101 = 101
+    ,e102 = 102
+    ,e103 = 103
+    ,e104 = 104
+    ,e105 = 105
+    ,e106 = 106
+    ,e107 = 107
+    ,e108 = 108
+    ,e109 = 109
+    ,e110 = 110
+    ,e111 = 111
+    ,e112 = 112
+    ,e113 = 113
+    ,e114 = 114
+    ,e115 = 115
+    ,e116 = 116
+    ,e117 = 117
+    ,e118 = 118
+    ,e119 = 119
+    ,e120 = 120
+    ,e121 = 121
+    ,e122 = 122
+    ,e123 = 123
+    ,e124 = 124
+    ,e125 = 125
+    ,e126 = 126
+    ,e127 = 127
+    ,e128 = 128
+    ,e129 = 129
+    ,e130 = 130
+    ,e131 = 131
+    ,e132 = 132
+    ,e133 = 133
+    ,e134 = 134
+    ,e135 = 135
+    ,e136 = 136
+    ,e137 = 137
+    ,e138 = 138
+    ,e139 = 139
+    ,e140 = 140
+    ,e141 = 141
+    ,e142 = 142
+    ,e143 = 143
+    ,e144 = 144
+    ,e145 = 145
+    ,e146 = 146
+    ,e147 = 147
+    ,e148 = 148
+    ,e149 = 149
+    ,e150 = 150
+    ,e151 = 151
+    ,e152 = 152
+    ,e153 = 153
+    ,e154 = 154
+    ,e155 = 155
+    ,e156 = 156
+    ,e157 = 157
+    ,e158 = 158
+    ,e159 = 159
+    ,e160 = 160
+    ,e161 = 161
+    ,e162 = 162
+    ,e163 = 163
+    ,e164 = 164
+    ,e165 = 165
+    ,e166 = 166
+    ,e167 = 167
+    ,e168 = 168
+    ,e169 = 169
+    ,e170 = 170
+    ,e171 = 171
+    ,e172 = 172
+    ,e173 = 173
+    ,e174 = 174
+    ,e175 = 175
+    ,e176 = 176
+    ,e177 = 177
+    ,e178 = 178
+    ,e179 = 179
+    ,e180 = 180
+    ,e181 = 181
+    ,e182 = 182
+    ,e183 = 183
+    ,e184 = 184
+    ,e185 = 185
+    ,e186 = 186
+    ,e187 = 187
+    ,e188 = 188
+    ,e189 = 189
+    ,e190 = 190
+    ,e191 = 191
+    ,e192 = 192
+    ,e193 = 193
+    ,e194 = 194
+    };
+} // namespace superior
 
 #endif // sample_hpp



reply via email to

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