lua_parameter_dictionary.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2016 The Cartographer Authors
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 // When a LuaParameterDictionary is constructed, a new Lua state (i.e. an
00018 // independent Lua interpreter) is fired up to evaluate the Lua code. The code
00019 // is expected to return a Lua table that contains key/value pairs that are the
00020 // key/value pairs of our parameter dictionary.
00021 //
00022 // We keep the Lua interpreter around and the table on the stack and reference
00023 // it in the Get* methods instead of moving its contents from Lua into a C++ map
00024 // since we can only know in the Get* methods what data type to expect in the
00025 // table.
00026 //
00027 // Some of the methods below documentation the current stack with the following
00028 // notation: S: <bottom> ... <top>
00029 
00030 #include "cartographer/common/lua_parameter_dictionary.h"
00031 
00032 #include <algorithm>
00033 #include <cmath>
00034 #include <functional>
00035 #include <memory>
00036 
00037 namespace cartographer {
00038 namespace common {
00039 
00040 namespace {
00041 
00042 // Replace the string at the top of the stack through a quoted version that Lua
00043 // can read back.
00044 void QuoteStringOnStack(lua_State* L) {
00045   CHECK(lua_isstring(L, -1)) << "Top of stack is not a string value.";
00046   int current_index = lua_gettop(L);
00047 
00048   // S: ... string
00049   lua_pushglobaltable(L);         // S: ... string globals
00050   lua_getfield(L, -1, "string");  // S: ... string globals <string module>
00051   lua_getfield(L, -1,
00052                "format");   // S: ... string globals <string module> format
00053   lua_pushstring(L, "%q");  // S: ... string globals <string module> format "%q"
00054   lua_pushvalue(L, current_index);  // S: ... string globals <string module>
00055                                     // format "%q" string
00056 
00057   lua_call(L, 2, 1);  // S: ... string globals <string module> quoted
00058   lua_replace(L, current_index);  // S: ... quoted globals <string module>
00059 
00060   lua_pop(L, 2);  // S: ... quoted
00061 }
00062 
00063 // Sets the given 'dictionary' as the value of the "this" key in Lua's registry
00064 // table.
00065 void SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {
00066   lua_pushstring(L, "this");
00067   lua_pushlightuserdata(L, dictionary);
00068   lua_settable(L, LUA_REGISTRYINDEX);
00069 }
00070 
00071 // Gets the 'dictionary' from the "this" key in Lua's registry table.
00072 LuaParameterDictionary* GetDictionaryFromRegistry(lua_State* L) {
00073   lua_pushstring(L, "this");
00074   lua_gettable(L, LUA_REGISTRYINDEX);
00075   void* return_value = lua_isnil(L, -1) ? nullptr : lua_touserdata(L, -1);
00076   lua_pop(L, 1);
00077   CHECK(return_value != nullptr);
00078   return reinterpret_cast<LuaParameterDictionary*>(return_value);
00079 }
00080 
00081 // CHECK()s if a Lua method returned an error.
00082 void CheckForLuaErrors(lua_State* L, int status) {
00083   CHECK_EQ(status, 0) << lua_tostring(L, -1);
00084 }
00085 
00086 // Returns 'a' if 'condition' is true, else 'b'.
00087 int LuaChoose(lua_State* L) {
00088   CHECK_EQ(lua_gettop(L), 3) << "choose() takes (condition, a, b).";
00089   CHECK(lua_isboolean(L, 1)) << "condition is not a boolean value.";
00090 
00091   const bool condition = lua_toboolean(L, 1);
00092   if (condition) {
00093     lua_pushvalue(L, 2);
00094   } else {
00095     lua_pushvalue(L, 3);
00096   }
00097   return 1;
00098 }
00099 
00100 // Pushes a value to the Lua stack.
00101 void PushValue(lua_State* L, const int key) { lua_pushinteger(L, key); }
00102 void PushValue(lua_State* L, const std::string& key) {
00103   lua_pushstring(L, key.c_str());
00104 }
00105 
00106 // Reads the value with the given 'key' from the Lua dictionary and pushes it to
00107 // the top of the stack.
00108 template <typename T>
00109 void GetValueFromLuaTable(lua_State* L, const T& key) {
00110   PushValue(L, key);
00111   lua_rawget(L, -2);
00112 }
00113 
00114 // CHECK() that the topmost parameter on the Lua stack is a table.
00115 void CheckTableIsAtTopOfStack(lua_State* L) {
00116   CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
00117 }
00118 
00119 // Returns true if 'key' is in the table at the top of the Lua stack.
00120 template <typename T>
00121 bool HasKeyOfType(lua_State* L, const T& key) {
00122   CheckTableIsAtTopOfStack(L);
00123   PushValue(L, key);
00124   lua_rawget(L, -2);
00125   const bool key_not_found = lua_isnil(L, -1);
00126   lua_pop(L, 1);  // Pop the item again.
00127   return !key_not_found;
00128 }
00129 
00130 // Iterates over the integer keys of the table at the top of the stack of 'L•
00131 // and pushes the values one by one. 'pop_value' is expected to pop a value and
00132 // put them into a C++ container.
00133 void GetArrayValues(lua_State* L, const std::function<void()>& pop_value) {
00134   int idx = 1;
00135   while (true) {
00136     GetValueFromLuaTable(L, idx);
00137     if (lua_isnil(L, -1)) {
00138       lua_pop(L, 1);
00139       break;
00140     }
00141     pop_value();
00142     ++idx;
00143   }
00144 }
00145 
00146 }  // namespace
00147 
00148 std::unique_ptr<LuaParameterDictionary>
00149 LuaParameterDictionary::NonReferenceCounted(
00150     const std::string& code, std::unique_ptr<FileResolver> file_resolver) {
00151   return std::unique_ptr<LuaParameterDictionary>(new LuaParameterDictionary(
00152       code, ReferenceCount::NO, std::move(file_resolver)));
00153 }
00154 
00155 LuaParameterDictionary::LuaParameterDictionary(
00156     const std::string& code, std::unique_ptr<FileResolver> file_resolver)
00157     : LuaParameterDictionary(code, ReferenceCount::YES,
00158                              std::move(file_resolver)) {}
00159 
00160 LuaParameterDictionary::LuaParameterDictionary(
00161     const std::string& code, ReferenceCount reference_count,
00162     std::unique_ptr<FileResolver> file_resolver)
00163     : L_(luaL_newstate()),
00164       index_into_reference_table_(-1),
00165       file_resolver_(std::move(file_resolver)),
00166       reference_count_(reference_count) {
00167   CHECK_NOTNULL(L_);
00168   SetDictionaryInRegistry(L_, this);
00169 
00170   luaL_openlibs(L_);
00171 
00172   lua_register(L_, "choose", LuaChoose);
00173   lua_register(L_, "include", LuaInclude);
00174   lua_register(L_, "read", LuaRead);
00175 
00176   CheckForLuaErrors(L_, luaL_loadstring(L_, code.c_str()));
00177   CheckForLuaErrors(L_, lua_pcall(L_, 0, 1, 0));
00178   CheckTableIsAtTopOfStack(L_);
00179 }
00180 
00181 LuaParameterDictionary::LuaParameterDictionary(
00182     lua_State* const L, ReferenceCount reference_count,
00183     std::shared_ptr<FileResolver> file_resolver)
00184     : L_(lua_newthread(L)),
00185       file_resolver_(std::move(file_resolver)),
00186       reference_count_(reference_count) {
00187   CHECK_NOTNULL(L_);
00188 
00189   // Make sure this is never garbage collected.
00190   CHECK(lua_isthread(L, -1));
00191   index_into_reference_table_ = luaL_ref(L, LUA_REGISTRYINDEX);
00192 
00193   CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
00194   lua_xmove(L, L_, 1);  // Moves the table and the coroutine over.
00195   CheckTableIsAtTopOfStack(L_);
00196 }
00197 
00198 LuaParameterDictionary::~LuaParameterDictionary() {
00199   if (reference_count_ == ReferenceCount::YES) {
00200     CheckAllKeysWereUsedExactlyOnceAndReset();
00201   }
00202   if (index_into_reference_table_ > 0) {
00203     luaL_unref(L_, LUA_REGISTRYINDEX, index_into_reference_table_);
00204   } else {
00205     lua_close(L_);
00206   }
00207 }
00208 
00209 std::vector<std::string> LuaParameterDictionary::GetKeys() const {
00210   CheckTableIsAtTopOfStack(L_);
00211   std::vector<std::string> keys;
00212 
00213   lua_pushnil(L_);  // Push the first key
00214   while (lua_next(L_, -2) != 0) {
00215     lua_pop(L_, 1);  // Pop value, keep key.
00216     if (!lua_isnumber(L_, -1)) {
00217       keys.emplace_back(lua_tostring(L_, -1));
00218     }
00219   }
00220   return keys;
00221 }
00222 
00223 bool LuaParameterDictionary::HasKey(const std::string& key) const {
00224   return HasKeyOfType(L_, key);
00225 }
00226 
00227 std::string LuaParameterDictionary::GetString(const std::string& key) {
00228   CheckHasKeyAndReference(key);
00229   GetValueFromLuaTable(L_, key);
00230   return PopString(Quoted::NO);
00231 }
00232 
00233 std::string LuaParameterDictionary::PopString(Quoted quoted) const {
00234   CHECK(lua_isstring(L_, -1)) << "Top of stack is not a string value.";
00235   if (quoted == Quoted::YES) {
00236     QuoteStringOnStack(L_);
00237   }
00238 
00239   const std::string value = lua_tostring(L_, -1);
00240   lua_pop(L_, 1);
00241   return value;
00242 }
00243 
00244 double LuaParameterDictionary::GetDouble(const std::string& key) {
00245   CheckHasKeyAndReference(key);
00246   GetValueFromLuaTable(L_, key);
00247   return PopDouble();
00248 }
00249 
00250 double LuaParameterDictionary::PopDouble() const {
00251   CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
00252   const double value = lua_tonumber(L_, -1);
00253   lua_pop(L_, 1);
00254   return value;
00255 }
00256 
00257 int LuaParameterDictionary::GetInt(const std::string& key) {
00258   CheckHasKeyAndReference(key);
00259   GetValueFromLuaTable(L_, key);
00260   return PopInt();
00261 }
00262 
00263 int LuaParameterDictionary::PopInt() const {
00264   CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
00265   const int value = lua_tointeger(L_, -1);
00266   lua_pop(L_, 1);
00267   return value;
00268 }
00269 
00270 bool LuaParameterDictionary::GetBool(const std::string& key) {
00271   CheckHasKeyAndReference(key);
00272   GetValueFromLuaTable(L_, key);
00273   return PopBool();
00274 }
00275 
00276 bool LuaParameterDictionary::PopBool() const {
00277   CHECK(lua_isboolean(L_, -1)) << "Top of stack is not a boolean value.";
00278   const bool value = lua_toboolean(L_, -1);
00279   lua_pop(L_, 1);
00280   return value;
00281 }
00282 
00283 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::GetDictionary(
00284     const std::string& key) {
00285   CheckHasKeyAndReference(key);
00286   GetValueFromLuaTable(L_, key);
00287   return PopDictionary(reference_count_);
00288 }
00289 
00290 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::PopDictionary(
00291     ReferenceCount reference_count) const {
00292   CheckTableIsAtTopOfStack(L_);
00293   std::unique_ptr<LuaParameterDictionary> value(
00294       new LuaParameterDictionary(L_, reference_count, file_resolver_));
00295   // The constructor lua_xmove()s the value, no need to pop it.
00296   CheckTableIsAtTopOfStack(L_);
00297   return value;
00298 }
00299 
00300 std::string LuaParameterDictionary::DoToString(
00301     const std::string& indent) const {
00302   std::string result = "{";
00303   bool dictionary_is_empty = true;
00304 
00305   const auto top_of_stack_to_string = [this, indent,
00306                                        &dictionary_is_empty]() -> std::string {
00307     dictionary_is_empty = false;
00308 
00309     const int value_type = lua_type(L_, -1);
00310     switch (value_type) {
00311       case LUA_TBOOLEAN:
00312         return PopBool() ? "true" : "false";
00313         break;
00314       case LUA_TSTRING:
00315         return PopString(Quoted::YES);
00316         break;
00317       case LUA_TNUMBER: {
00318         const double value = PopDouble();
00319         if (std::isinf(value)) {
00320           return value < 0 ? "-math.huge" : "math.huge";
00321         } else {
00322           return std::to_string(value);
00323         }
00324       } break;
00325       case LUA_TTABLE: {
00326         std::unique_ptr<LuaParameterDictionary> subdict(
00327             PopDictionary(ReferenceCount::NO));
00328         return subdict->DoToString(indent + "  ");
00329       } break;
00330       default:
00331         LOG(FATAL) << "Unhandled type " << lua_typename(L_, value_type);
00332     }
00333   };
00334 
00335   // Integer (array) keys.
00336   for (int i = 1; i; ++i) {
00337     GetValueFromLuaTable(L_, i);
00338     if (lua_isnil(L_, -1)) {
00339       lua_pop(L_, 1);
00340       break;
00341     }
00342     result.append("\n");
00343     result.append(indent);
00344     result.append("  ");
00345     result.append(top_of_stack_to_string());
00346     result.append(",");
00347   }
00348 
00349   // String keys.
00350   std::vector<std::string> keys = GetKeys();
00351   if (!keys.empty()) {
00352     std::sort(keys.begin(), keys.end());
00353     for (const std::string& key : keys) {
00354       GetValueFromLuaTable(L_, key);
00355       result.append("\n");
00356       result.append(indent);
00357       result.append("  ");
00358       result.append(key);
00359       result.append(" = ");
00360       result.append(top_of_stack_to_string());
00361       result.append(",");
00362     }
00363   }
00364   result.append("\n");
00365   result.append(indent);
00366   result.append("}");
00367 
00368   if (dictionary_is_empty) {
00369     return "{}";
00370   }
00371   return result;
00372 }
00373 
00374 std::string LuaParameterDictionary::ToString() const { return DoToString(""); }
00375 
00376 std::vector<double> LuaParameterDictionary::GetArrayValuesAsDoubles() {
00377   std::vector<double> values;
00378   GetArrayValues(L_, [&values, this] { values.push_back(PopDouble()); });
00379   return values;
00380 }
00381 
00382 std::vector<std::unique_ptr<LuaParameterDictionary>>
00383 LuaParameterDictionary::GetArrayValuesAsDictionaries() {
00384   std::vector<std::unique_ptr<LuaParameterDictionary>> values;
00385   GetArrayValues(L_, [&values, this] {
00386     values.push_back(PopDictionary(reference_count_));
00387   });
00388   return values;
00389 }
00390 
00391 std::vector<std::string> LuaParameterDictionary::GetArrayValuesAsStrings() {
00392   std::vector<std::string> values;
00393   GetArrayValues(L_,
00394                  [&values, this] { values.push_back(PopString(Quoted::NO)); });
00395   return values;
00396 }
00397 
00398 void LuaParameterDictionary::CheckHasKey(const std::string& key) const {
00399   CHECK(HasKey(key)) << "Key '" << key << "' not in dictionary:\n"
00400                      << ToString();
00401 }
00402 
00403 void LuaParameterDictionary::CheckHasKeyAndReference(const std::string& key) {
00404   CheckHasKey(key);
00405   reference_counts_[key]++;
00406 }
00407 
00408 void LuaParameterDictionary::CheckAllKeysWereUsedExactlyOnceAndReset() {
00409   for (const auto& key : GetKeys()) {
00410     CHECK_EQ(1, reference_counts_.count(key))
00411         << "Key '" << key << "' was used the wrong number of times.";
00412     CHECK_EQ(1, reference_counts_.at(key))
00413         << "Key '" << key << "' was used the wrong number of times.";
00414   }
00415   reference_counts_.clear();
00416 }
00417 
00418 int LuaParameterDictionary::GetNonNegativeInt(const std::string& key) {
00419   const int temp = GetInt(key);  // Will increase reference count.
00420   CHECK_GE(temp, 0) << temp << " is negative.";
00421   return temp;
00422 }
00423 
00424 // Lua function to run a script in the current Lua context. Takes the filename
00425 // as its only argument.
00426 int LuaParameterDictionary::LuaInclude(lua_State* L) {
00427   CHECK_EQ(lua_gettop(L), 1);
00428   CHECK(lua_isstring(L, -1)) << "include takes a filename.";
00429 
00430   LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
00431   const std::string basename = lua_tostring(L, -1);
00432   const std::string filename =
00433       parameter_dictionary->file_resolver_->GetFullPathOrDie(basename);
00434   if (std::find(parameter_dictionary->included_files_.begin(),
00435                 parameter_dictionary->included_files_.end(),
00436                 filename) != parameter_dictionary->included_files_.end()) {
00437     std::string error_msg =
00438         "Tried to include " + filename +
00439         " twice. Already included files in order of inclusion: ";
00440     for (const std::string& filename : parameter_dictionary->included_files_) {
00441       error_msg.append(filename);
00442       error_msg.append("\n");
00443     }
00444     LOG(FATAL) << error_msg;
00445   }
00446   parameter_dictionary->included_files_.push_back(filename);
00447   lua_pop(L, 1);
00448   CHECK_EQ(lua_gettop(L), 0);
00449 
00450   const std::string content =
00451       parameter_dictionary->file_resolver_->GetFileContentOrDie(basename);
00452   CheckForLuaErrors(
00453       L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));
00454   CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));
00455 
00456   return lua_gettop(L);
00457 }
00458 
00459 // Lua function to read a file into a string.
00460 int LuaParameterDictionary::LuaRead(lua_State* L) {
00461   CHECK_EQ(lua_gettop(L), 1);
00462   CHECK(lua_isstring(L, -1)) << "read takes a filename.";
00463 
00464   LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
00465   const std::string file_content =
00466       parameter_dictionary->file_resolver_->GetFileContentOrDie(
00467           lua_tostring(L, -1));
00468   lua_pushstring(L, file_content.c_str());
00469   return 1;
00470 }
00471 
00472 }  // namespace common
00473 }  // namespace cartographer


cartographer
Author(s): The Cartographer Authors
autogenerated on Thu May 9 2019 02:27:35