[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