<commit_before>/*
*   Copyright 2020 Google LLC
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       https://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/

#include <filesystem>
#include <fstream>

#include "../src/proto_value.h"
#include "../src/proto_parser.h"
#include "proto/event_predicate.pb.h"

using ::wireless_android_play_analytics::ProtoValue;
using ::wireless_android_play_analytics::MessageValue;
using ::wireless_android_play_analytics::PrimitiveValue;
using ::wireless_android_play_analytics::EventPredicate;
using ::wireless_android_play_analytics::ProtoParser;

namespace {

class EventPredicateConverter {
 public:

  std::unique_ptr<ProtoValue> ConvertEventPredicateFromFile(
      absl::string_view path) {
    ProtoParser parser(ReadTextProtoFromStream(path));
    EventPredicate event_predicate_prototype;
    std::unique_ptr<ProtoValue> and_field =
        absl::make_unique<MessageValue>("and", 0, 0);
    MessageValue* and_field_ptr = static_cast<MessageValue*>(and_field.get());

    std::unique_ptr<ProtoValue> event_predicate =
        parser.Create(event_predicate_prototype);
    MessageValue* event_predicate_ptr =
        static_cast<MessageValue*>(event_predicate.get());
    const std::vector<std::unique_ptr<ProtoValue>>& fields =
      event_predicate_ptr->GetFields();

    for (const std::unique_ptr<ProtoValue>& field : fields) {
      if (field.get()->GetName() == "event_matcher") {
        absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
            GetFieldNameToPtrMap(*field.get());
        if (name_to_ptr_map.find("int_comparator") == name_to_ptr_map.end()) {
          and_field_ptr->AddField(EventMatcherToFieldExists(*field.get()));
        } else {
          and_field_ptr->AddField(EventMatcherToFieldValue(*field.get()));
        }

      } else if (field.get()->GetName() == "ui_element_path_predicate") {
        and_field_ptr->AddField(UiElementPathPredicateToUiElement(*field.get()));
      }
    }

    return and_field;
  }

 private:

  // Reads text proto into a string from a file.
  std::string ReadTextProtoFromStream(absl::string_view path) {
    std::ifstream ifs;
    std::string text_proto;

    ifs.open(std::string(path));

    assert(ifs.good());

    while (ifs.good()) {
      std::string tmp;
      std::getline(ifs, tmp);
      text_proto += tmp + "\n";
    }

    return text_proto;
  }

  // Copies the comments of an original proto value to a copy, if they're both
  // primitive types also copy the value.
  void CopyVal(ProtoValue* copy, const ProtoValue& original) {
    copy->SetCommentBehindField(original.GetCommentBehindField());
    copy->SetCommentAboveField(original.GetCommentAboveField());
    PrimitiveValue* copy_val = dynamic_cast<PrimitiveValue*>(copy);
    const PrimitiveValue* original_val = dynamic_cast<const PrimitiveValue*>(
        &original);
    if (copy_val != nullptr && original_val != nullptr) {
      // Copy their values only if they are both primitive values.
      copy_val->SetVal(original_val->GetVal());
    }
  }

  // Gets a hash table that maps field names of a message value to their
  // corresponding protovalue pointer.
  absl::flat_hash_map<std::string, ProtoValue*> GetFieldNameToPtrMap(
      const ProtoValue& original) {
    const MessageValue* original_ptr =
        static_cast<const MessageValue*>(&original);
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map;
    const std::vector<std::unique_ptr<ProtoValue>>& fields =
        original_ptr->GetFields();
    for (const std::unique_ptr<ProtoValue>& field : fields) {
      name_to_ptr_map[field.get()->GetName()] = field.get();
    }

    return name_to_ptr_map;
  }

  // A universal converter for message matcher into either index_ui,
  // field_value, or field_exists.
  std::unique_ptr<ProtoValue> MessageMatcherUniversalConverter(
      const ProtoValue& original, absl::string_view name,
      const ProtoValue* index = nullptr) {
    std::unique_ptr<ProtoValue> constructed_field = nullptr;
    // Get the name of each field on the original proto.
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);

    if (name == "index_ui" || name == "field_value" || name == "leaf_ui") {
      constructed_field = absl::make_unique<MessageValue>(name,
          original.GetIndentCount() + 1, 0);
      MessageValue* constructed_field_ptr = static_cast<MessageValue*>(
          constructed_field.get());
      if (name == "index_ui") {
        // Index UI has an extra field of index.
        std::unique_ptr<ProtoValue> index_field =
            absl::make_unique<PrimitiveValue>("index",
            original.GetIndentCount() + 2, 0);
        CopyVal(index_field.get(), *index);
        constructed_field_ptr->AddField(std::move(index_field));
      }
      std::unique_ptr<ProtoValue> path = absl::make_unique<PrimitiveValue>(
        "path", original.GetIndentCount() + 2, 1);
      CopyVal(path.get(), *name_to_ptr_map["input_field"]);
      std::unique_ptr<ProtoValue> equals_int =
          absl::make_unique<PrimitiveValue>("equals_int",
          original.GetIndentCount() + 2, 2);
      CopyVal(equals_int.get(), *name_to_ptr_map["int_comparator"]);
      constructed_field_ptr->AddField(std::move(path));
      constructed_field_ptr->AddField(std::move(equals_int));
    } else {
      constructed_field = absl::make_unique<PrimitiveValue>(name,
          original.GetIndentCount() + 1, 0);
      PrimitiveValue* constructed_field_ptr = static_cast<PrimitiveValue*>(
          constructed_field.get());
      PrimitiveValue* input_field_ptr = static_cast<PrimitiveValue*>(
          name_to_ptr_map["input_field"]);
      constructed_field_ptr->SetVal(input_field_ptr->GetVal());
    }

    // Copy the comments of the original proto to the constructed field
    CopyVal(constructed_field.get(), original);

    return constructed_field;
  }

  std::unique_ptr<ProtoValue> LeafUiPredicateToLeafUi(
      const ProtoValue& original) {
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);
    return MessageMatcherUniversalConverter(
        *name_to_ptr_map["ui_element_matcher"], "leaf_ui");
  }

  std::unique_ptr<ProtoValue> IndexUiPredicateToIndexUi(
      const ProtoValue& original) {
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);
    return MessageMatcherUniversalConverter(
        *name_to_ptr_map["ui_element_matcher"], "index_ui",
        name_to_ptr_map["index"]);
  }

  std::unique_ptr<ProtoValue> EventMatcherToFieldExists(
      const ProtoValue& original) {
    return MessageMatcherUniversalConverter(original, "field_exists");
  }

  std::unique_ptr<ProtoValue> EventMatcherToFieldValue(
      const ProtoValue& original) {
    return MessageMatcherUniversalConverter(original, "field_value");
  }

  std::unique_ptr<ProtoValue> UiElementPathPredicateToUiElement(
      const ProtoValue& original) {
    std::unique_ptr<ProtoValue> ui_element = absl::make_unique<MessageValue>(
        "ui_element", original.GetIndentCount() + 1, 0);
    MessageValue* ui_element_ptr = static_cast<MessageValue*>(ui_element.get());
    CopyVal(ui_element.get(), original);

    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);

    // Convert ui_element_type to type
    std::unique_ptr<ProtoValue> type = absl::make_unique<PrimitiveValue>(
        "type", name_to_ptr_map["ui_element_type"]->GetIndentCount() + 1,
        0);
    CopyVal(type.get(), *name_to_ptr_map["ui_element_type"]);
    ui_element_ptr->AddField(std::move(type));

    // Create nested predicate only if they exist.
    if (name_to_ptr_map.size() > 1) {
      std::unique_ptr<ProtoValue> and_field = absl::make_unique<MessageValue>(
          "and", original.GetIndentCount() + 2, 1);
      MessageValue* and_field_ptr = static_cast<MessageValue*>(and_field.get());
      if (name_to_ptr_map.find("leaf_ui_predicate") != name_to_ptr_map.end()) {
        and_field_ptr->AddField(LeafUiPredicateToLeafUi(
            *name_to_ptr_map["leaf_ui_predicate"]));
      }
      if (name_to_ptr_map.find("index_ui_matcher") != name_to_ptr_map.end()) {
        and_field_ptr->AddField(IndexUiPredicateToIndexUi(
            *name_to_ptr_map["index_ui_matcher"]));
      }
      ui_element_ptr->AddField(std::move(and_field));
    }

    return ui_element;
  }
};

} // namespace

int main() {
  EventPredicateConverter converter;
  std::unique_ptr<ProtoValue> output =
      converter.ConvertEventPredicateFromFile(
      "demo/proto_texts/original_proto_text.txt");
  std::cout << output.get()->PrintToTextProto();
  return 0;
}<commit_msg>added comments to make the job of each function clearer<commit_after>/*
*   Copyright 2020 Google LLC
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       https://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/

#include <filesystem>
#include <fstream>

#include "../src/proto_value.h"
#include "../src/proto_parser.h"
#include "proto/event_predicate.pb.h"

using ::wireless_android_play_analytics::ProtoValue;
using ::wireless_android_play_analytics::MessageValue;
using ::wireless_android_play_analytics::PrimitiveValue;
using ::wireless_android_play_analytics::EventPredicate;
using ::wireless_android_play_analytics::ProtoParser;

namespace {

class EventPredicateConverter {
 public:

  // Original:
  //           # comment
  //           # to
  //           # save

  //           # Condition 1
  //           event_matcher {
  //             input_field: "PlayExtension.store.click"
  //           }
  //           # Condition 2
  //           event_matcher {
  //             input_field: "PlayExtension.log_source"
  //             int_comparator: 65  # WEB_STORE
  //           }
  //           # Condition 3
  //           ui_element_path_predicate {
  //             ui_element_type: STORE_UI_ELEMENT
  //             # Condition 3.1
  //             leaf_ui_predicate {
  //               # ...
  //               ui_element_type: STORE_UI_ELEMENT
  //               ui_element_matcher {
  //                 input_field: "type"
  //                 int_comparator: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //               }
  //             }
  //             # Condition 3.2
  //             index_ui_matcher {
  //               ui_element_type: STORE_UI_ELEMENT
  //               index: -2
  //               ui_element_matcher {
  //                 input_field: "type"
  //                 int_comparator: 1  # SOMETHING
  //               }
  //             }
  //           }
  // Modified:
  //           and {
  //             # Condition 1
  //             field_exists: "PlayExtension.store.click"
  //             # Condition 2
  //             field_value {
  //               path: "PlayExtension.log_source"
  //               equals_int: 65  # WEB_STORE
  //             }
  //             # Condition 3
  //             ui_element {
  //               type: STORE_UI_ELEMENT
  //               and {
  //                 # Condition 3.1
  //                 leaf_ui {
  //                   path:  "type"
  //                   equals_int: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //                 }
  //                 # Condition 3.2
  //                 index_ui {
  //                   index: -2
  //                   path: "type"
  //                   equals_int: 1  # SOMETHING
  //                 }
  //               }
  //             }
  //           }
  std::unique_ptr<ProtoValue> ConvertEventPredicateFromFile(
      absl::string_view path) {
    ProtoParser parser(ReadTextProtoFromStream(path));
    EventPredicate event_predicate_prototype;
    std::unique_ptr<ProtoValue> and_field =
        absl::make_unique<MessageValue>("and", 0, 0);
    MessageValue* and_field_ptr = static_cast<MessageValue*>(and_field.get());

    std::unique_ptr<ProtoValue> event_predicate =
        parser.Create(event_predicate_prototype);
    MessageValue* event_predicate_ptr =
        static_cast<MessageValue*>(event_predicate.get());
    const std::vector<std::unique_ptr<ProtoValue>>& fields =
      event_predicate_ptr->GetFields();

    for (const std::unique_ptr<ProtoValue>& field : fields) {
      if (field.get()->GetName() == "event_matcher") {
        absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
            GetFieldNameToPtrMap(*field.get());
        if (name_to_ptr_map.find("int_comparator") == name_to_ptr_map.end()) {
          and_field_ptr->AddField(EventMatcherToFieldExists(*field.get()));
        } else {
          and_field_ptr->AddField(EventMatcherToFieldValue(*field.get()));
        }

      } else if (field.get()->GetName() == "ui_element_path_predicate") {
        and_field_ptr->AddField(UiElementPathPredicateToUiElement(*field.get()));
      }
    }

    return and_field;
  }

 private:

  // Reads text proto into a string from a file.
  std::string ReadTextProtoFromStream(absl::string_view path) {
    std::ifstream ifs;
    std::string text_proto;

    ifs.open(std::string(path));

    assert(ifs.good());

    while (ifs.good()) {
      std::string tmp;
      std::getline(ifs, tmp);
      text_proto += tmp + "\n";
    }

    return text_proto;
  }

  // Copies the comments of an original proto value to a copy, if they're both
  // primitive types also copy the value.
  void CopyVal(ProtoValue* copy, const ProtoValue& original) {
    copy->SetCommentBehindField(original.GetCommentBehindField());
    copy->SetCommentAboveField(original.GetCommentAboveField());
    PrimitiveValue* copy_val = dynamic_cast<PrimitiveValue*>(copy);
    const PrimitiveValue* original_val = dynamic_cast<const PrimitiveValue*>(
        &original);
    if (copy_val != nullptr && original_val != nullptr) {
      // Copy their values only if they are both primitive values.
      copy_val->SetVal(original_val->GetVal());
    }
  }

  // Gets a hash table that maps field names of a message value to their
  // corresponding protovalue pointer.
  absl::flat_hash_map<std::string, ProtoValue*> GetFieldNameToPtrMap(
      const ProtoValue& original) {
    const MessageValue* original_ptr =
        static_cast<const MessageValue*>(&original);
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map;
    const std::vector<std::unique_ptr<ProtoValue>>& fields =
        original_ptr->GetFields();
    for (const std::unique_ptr<ProtoValue>& field : fields) {
      name_to_ptr_map[field.get()->GetName()] = field.get();
    }

    return name_to_ptr_map;
  }

  // A universal converter for message matcher into either index_ui,
  // field_value, or field_exists.
  std::unique_ptr<ProtoValue> MessageMatcherUniversalConverter(
      const ProtoValue& original, absl::string_view name,
      const ProtoValue* index = nullptr) {
    std::unique_ptr<ProtoValue> constructed_field = nullptr;
    // Get the name of each field on the original proto.
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);

    if (name == "index_ui" || name == "field_value" || name == "leaf_ui") {
      constructed_field = absl::make_unique<MessageValue>(name,
          original.GetIndentCount() + 1, 0);
      MessageValue* constructed_field_ptr = static_cast<MessageValue*>(
          constructed_field.get());
      if (name == "index_ui") {
        // Index UI has an extra field of index.
        std::unique_ptr<ProtoValue> index_field =
            absl::make_unique<PrimitiveValue>("index",
            original.GetIndentCount() + 2, 0);
        CopyVal(index_field.get(), *index);
        constructed_field_ptr->AddField(std::move(index_field));
      }
      std::unique_ptr<ProtoValue> path = absl::make_unique<PrimitiveValue>(
        "path", original.GetIndentCount() + 2, 1);
      CopyVal(path.get(), *name_to_ptr_map["input_field"]);
      std::unique_ptr<ProtoValue> equals_int =
          absl::make_unique<PrimitiveValue>("equals_int",
          original.GetIndentCount() + 2, 2);
      CopyVal(equals_int.get(), *name_to_ptr_map["int_comparator"]);
      constructed_field_ptr->AddField(std::move(path));
      constructed_field_ptr->AddField(std::move(equals_int));
    } else {
      constructed_field = absl::make_unique<PrimitiveValue>(name,
          original.GetIndentCount() + 1, 0);
      PrimitiveValue* constructed_field_ptr = static_cast<PrimitiveValue*>(
          constructed_field.get());
      PrimitiveValue* input_field_ptr = static_cast<PrimitiveValue*>(
          name_to_ptr_map["input_field"]);
      constructed_field_ptr->SetVal(input_field_ptr->GetVal());
    }

    // Copy the comments of the original proto to the constructed field
    CopyVal(constructed_field.get(), original);

    return constructed_field;
  }

  // Original:
  //            # Condition 3.1
  //            leaf_ui_predicate {
  //              # ...
  //              ui_element_type: STORE_UI_ELEMENT
  //              ui_element_matcher {
  //                input_field: "type"
  //                int_comparator: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //              }
  //            }
  // Modified:
  //            # Condition 3.1
  //            leaf_ui {
  //              path:  "type"
  //              equals_int: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //            }
  std::unique_ptr<ProtoValue> LeafUiPredicateToLeafUi(
      const ProtoValue& original) {
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);
    return MessageMatcherUniversalConverter(
        *name_to_ptr_map["ui_element_matcher"], "leaf_ui");
  }

  // Original:
  //            # Condition 3.2
  //            index_ui_matcher {
  //              ui_element_type: STORE_UI_ELEMENT
  //              index: -2
  //              ui_element_matcher {
  //                input_field: "type"
  //                int_comparator: 1  # SOMETHING
  //              }
  //            }
  // Modified:
  //            # Condition 3.2
  //            index_ui {
  //              index: -2
  //              path: "type"
  //              equals_int: 1  # SOMETHING
  //            }
  std::unique_ptr<ProtoValue> IndexUiPredicateToIndexUi(
      const ProtoValue& original) {
    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);
    return MessageMatcherUniversalConverter(
        *name_to_ptr_map["ui_element_matcher"], "index_ui",
        name_to_ptr_map["index"]);
  }

  // Original:
  //            # Condition 1
  //            event_matcher {
  //              input_field: "PlayExtension.store.click"
  //            }
  // Modified:
  //            # Condition 1
  //            field_exists: "PlayExtension.store.click"
  std::unique_ptr<ProtoValue> EventMatcherToFieldExists(
      const ProtoValue& original) {
    return MessageMatcherUniversalConverter(original, "field_exists");
  }

  // Original:
  //           # Condition 2
  //           event_matcher {
  //             input_field: "PlayExtension.log_source"
  //             int_comparator: 65  # WEB_STORE
  //           }
  // Modified:
  //           # Condition 2
  //           field_value {
  //             path: "PlayExtension.log_source"
  //             equals_int: 65  # WEB_STORE
  //           }
  std::unique_ptr<ProtoValue> EventMatcherToFieldValue(
      const ProtoValue& original) {
    return MessageMatcherUniversalConverter(original, "field_value");
  }

  // Original:
  //           # Condition 3
  //           ui_element_path_predicate {
  //             ui_element_type: STORE_UI_ELEMENT
  //             # Condition 3.1
  //             leaf_ui_predicate {
  //               # ...
  //               ui_element_type: STORE_UI_ELEMENT
  //               ui_element_matcher {
  //                 input_field: "type"
  //                 int_comparator: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //               }
  //             }
  //             # Condition 3.2
  //             index_ui_matcher {
  //               ui_element_type: STORE_UI_ELEMENT
  //               index: -2
  //               ui_element_matcher {
  //                 input_field: "type"
  //                 int_comparator: 1  # SOMETHING
  //               }
  //             }
  //           }
  // Modified:
  //           ui_element {
  //           type: STORE_UI_ELEMENT
  //           and {
  //             # Condition 3.1
  //             leaf_ui {
  //               path:  "type"
  //               equals_int: 7305  # AUTHENTICATION_OPT_OUT_CHECKED
  //             }
  //             # Condition 3.2
  //             index_ui {
  //               index: -2
  //               path: "type"
  //               equals_int: 1  # SOMETHING
  //             }
  //           }
  //         }
  std::unique_ptr<ProtoValue> UiElementPathPredicateToUiElement(
      const ProtoValue& original) {
    std::unique_ptr<ProtoValue> ui_element = absl::make_unique<MessageValue>(
        "ui_element", original.GetIndentCount() + 1, 0);
    MessageValue* ui_element_ptr = static_cast<MessageValue*>(ui_element.get());
    CopyVal(ui_element.get(), original);

    absl::flat_hash_map<std::string, ProtoValue*> name_to_ptr_map =
        GetFieldNameToPtrMap(original);

    // Convert ui_element_type to type
    std::unique_ptr<ProtoValue> type = absl::make_unique<PrimitiveValue>(
        "type", name_to_ptr_map["ui_element_type"]->GetIndentCount() + 1,
        0);
    CopyVal(type.get(), *name_to_ptr_map["ui_element_type"]);
    ui_element_ptr->AddField(std::move(type));

    // Create nested predicate only if they exist.
    if (name_to_ptr_map.size() > 1) {
      std::unique_ptr<ProtoValue> and_field = absl::make_unique<MessageValue>(
          "and", original.GetIndentCount() + 2, 1);
      MessageValue* and_field_ptr = static_cast<MessageValue*>(and_field.get());
      if (name_to_ptr_map.find("leaf_ui_predicate") != name_to_ptr_map.end()) {
        and_field_ptr->AddField(LeafUiPredicateToLeafUi(
            *name_to_ptr_map["leaf_ui_predicate"]));
      }
      if (name_to_ptr_map.find("index_ui_matcher") != name_to_ptr_map.end()) {
        and_field_ptr->AddField(IndexUiPredicateToIndexUi(
            *name_to_ptr_map["index_ui_matcher"]));
      }
      ui_element_ptr->AddField(std::move(and_field));
    }

    return ui_element;
  }
};

} // namespace

int main() {
  EventPredicateConverter converter;
  std::unique_ptr<ProtoValue> output =
      converter.ConvertEventPredicateFromFile(
      "demo/proto_texts/original_proto_text.txt");
  std::cout << output.get()->PrintToTextProto();
  return 0;
}<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
//
// The MIT License (MIT)
//
// Copyright (c) 2014 stevehalliwell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
////////////////////////////////////////////////////////////

#ifndef LUA_PARSER_HPP
#define LUA_PARSER_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////

#include <ResourceManager/ResourceData.hpp>
#include <ResourceManager\Logger.hpp>
#include <string>
#include <list>
#include <vector>
#include <algorithm>

extern "C" {
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
}

////////////////////////////////////////////////////////////
/// \brief LuaParser that takes in a path name, and returns a list<ResourceData>.
/// The Parser will open the lua table specifying a resource pack, parse its
/// contents and leaf resources or continue opening resources packs.
///
////////////////////////////////////////////////////////////

namespace rm
{

typedef std::list<ResourceData> ResourceDataList;

class LuaParser
{
public:

    ////////////////////////////////////////////////////////////
    /// \brief Parse resource pack from file and return resource data list
    ///
    /// \param path	File path from which to load the resource pack
    ///
    ////////////////////////////////////////////////////////////
    static ResourceDataList parsePack(const std::string& path);

private:
    ////////////////////////////////////////////////////////////
    /// \brief Parse individual resource pack from file and return resource data list
    ///
    /// \param path	File path from which to load the individual resource pack
    ///
    ////////////////////////////////////////////////////////////
    static ResourceDataList leafPack(const std::string& path);

    ////////////////////////////////////////////////////////////
    /// \brief Parse current individual resource pack leaf and return resource data
    ///
    ////////////////////////////////////////////////////////////
    static ResourceData parseLeaf();

	////////////////////////////////////////////////////////////
    /// Member data
    ////////////////////////////////////////////////////////////
	static lua_State* m_luaState; // Reference to Lua state
};

} // namespace rm

#endif //LUA_PARSER_HPP

///////////////////////////////////////////////////////////
/// \class LuaParser
/// \ingroup
/// rm::LuaParser is the class which handles loading in of useful data
/// from resource packs. These resource packs are lua tables. Each
/// lua table represents one resource pack and each pack is contained
/// in it's own file.
///
/// rm::LuaParser takes the topmost resource packs and evaluates it's
/// data, it then performs a breadth-first search through the tree structure
/// possibly specifed in the resource packs. As the parser finds resources
/// in the packs it creates a resource data which contains the relevant data,
/// this is then added to a list. Once the parser has discovered all the leafed
/// nodes it returns the list of resource datas to rm::ResourceManager.
///
/// Note that access to rm::LuaParser is intended to be restricted. Most
/// of the usage of rm::LuaParser should be handled by rm::ResourceManager.
////////////////////////////////////////////////////////////<commit_msg>Fixed invalid include<commit_after>////////////////////////////////////////////////////////////
//
// The MIT License (MIT)
//
// Copyright (c) 2014 stevehalliwell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
////////////////////////////////////////////////////////////

#ifndef LUA_PARSER_HPP
#define LUA_PARSER_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////

#include <ResourceManager/ResourceData.hpp>
#include <ResourceManager/Logger.hpp>
#include <string>
#include <list>
#include <vector>
#include <algorithm>

extern "C" {
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
}

////////////////////////////////////////////////////////////
/// \brief LuaParser that takes in a path name, and returns a list<ResourceData>.
/// The Parser will open the lua table specifying a resource pack, parse its
/// contents and leaf resources or continue opening resources packs.
///
////////////////////////////////////////////////////////////

namespace rm
{

typedef std::list<ResourceData> ResourceDataList;

class LuaParser
{
public:

    ////////////////////////////////////////////////////////////
    /// \brief Parse resource pack from file and return resource data list
    ///
    /// \param path	File path from which to load the resource pack
    ///
    ////////////////////////////////////////////////////////////
    static ResourceDataList parsePack(const std::string& path);

private:
    ////////////////////////////////////////////////////////////
    /// \brief Parse individual resource pack from file and return resource data list
    ///
    /// \param path	File path from which to load the individual resource pack
    ///
    ////////////////////////////////////////////////////////////
    static ResourceDataList leafPack(const std::string& path);

    ////////////////////////////////////////////////////////////
    /// \brief Parse current individual resource pack leaf and return resource data
    ///
    ////////////////////////////////////////////////////////////
    static ResourceData parseLeaf();

	////////////////////////////////////////////////////////////
    /// Member data
    ////////////////////////////////////////////////////////////
	static lua_State* m_luaState; // Reference to Lua state
};

} // namespace rm

#endif //LUA_PARSER_HPP

///////////////////////////////////////////////////////////
/// \class LuaParser
/// \ingroup
/// rm::LuaParser is the class which handles loading in of useful data
/// from resource packs. These resource packs are lua tables. Each
/// lua table represents one resource pack and each pack is contained
/// in it's own file.
///
/// rm::LuaParser takes the topmost resource packs and evaluates it's
/// data, it then performs a breadth-first search through the tree structure
/// possibly specifed in the resource packs. As the parser finds resources
/// in the packs it creates a resource data which contains the relevant data,
/// this is then added to a list. Once the parser has discovered all the leafed
/// nodes it returns the list of resource datas to rm::ResourceManager.
///
/// Note that access to rm::LuaParser is intended to be restricted. Most
/// of the usage of rm::LuaParser should be handled by rm::ResourceManager.
////////////////////////////////////////////////////////////<|endoftext|>


<commit_before>/****************************************************************************

    urid.hpp - support file for writing LV2 plugins in C++

    Copyright (C) 2012 Michael Fisher <mfisher31@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

****************************************************************************/

#ifndef LV2_URID_HPP
#define LV2_URID_HPP

#include <lv2/lv2plug.in/ns/ext/urid/urid.h>

namespace LV2 {

   /**
      The URID Extension.
      The actual type that your plugin class will inherit when you use
      this mixin is the internal struct template I.
      @ingroup pluginmixins
   */

   #define LV2MM_EXTENSION (slug) \
   LV2MM_MIXIN_CLASS $slug { \
      LV2MM_MIXIN_DERIVED {

   #define LV2MM_EXTENSION_END }; };

   LV2MM_MIXIN_CLASS URID {

     LV2MM_MIXIN_DERIVED {

         I() { }

         /** @internal */
         static void map_feature_handlers(FeatureHandlerMap& hmap)
         {
            hmap[LV2_URID__map] = &I<Derived>::handle_map_feature;
            hmap[LV2_URID__unmap] = &I<Derived>::handle_unmap_feature;
         }

         /** @internal */
         static void handle_map_feature(void* instance, void* data)
         {
            Derived* d = reinterpret_cast<Derived*>(instance);
            I<Derived>* fe = static_cast<I<Derived>*>(d);

            fe->p_map = reinterpret_cast<LV2_URID_Map*>(data);
            fe->m_ok = true;
         }

         /** @internal */
         static void handle_unmap_feature(void* instance, void* data)
         {
            Derived* d = reinterpret_cast<Derived*>(instance);
            I<Derived>* fe = static_cast<I<Derived>*>(d);

            fe->p_unmap = reinterpret_cast<LV2_URID_Unmap*> (data);
            fe->m_ok = true;
         }

         bool check_ok()
         {
            if (LV2MM_DEBUG) {
              std::clog<<"    [LV2::URID] Validation "
                       <<(this->m_ok ? "succeeded" : "failed")<<"."<<std::endl;
            }
            return this->m_ok;
         }

     protected:

         /**
            Get the URI for a previously mapped numeric ID.

            Returns NULL if @p urid is not yet mapped.  Otherwise, the corresponding
            URI is returned in a canonical form.  This MAY not be the exact same
            string that was originally passed to LV2_URID_Map::map(), but it MUST be
            an identical URI according to the URI syntax specification (RFC3986).  A
            non-NULL return for a given @p urid will always be the same for the life
            of the plugin.  Plugins that intend to perform string comparison on
            unmapped URIs SHOULD first canonicalise URI strings with a call to
            map_uri() followed by a call to unmap_uri().

            @param urid The ID to be mapped back to the URI string.
         */
         const char* unmap (LV2_URID urid)
         {
            return p_unmap->unmap (p_unmap->handle, urid);
         }

         /**
            Get the numeric ID of a URI.

            If the ID does not already exist, it will be created.

            This function is referentially transparent; any number of calls with the
            same arguments is guaranteed to return the same value over the life of a
            plugin instance.  Note, however, that several URIs MAY resolve to the
            same ID if the host considers those URIs equivalent.

            This function is not necessarily very fast or RT-safe: plugins SHOULD
            cache any IDs they might need in performance critical situations.

            The return value 0 is reserved and indicates that an ID for that URI
            could not be created for whatever reason.  However, hosts SHOULD NOT
            return 0 from this function in non-exceptional circumstances (i.e. the
            URI map SHOULD be dynamic).

            @param uri The URI to be mapped to an integer ID.
         */
         LV2_URID map (const char* uri)
         {
            return p_map->map (p_map->handle, uri);
         }

         LV2_URID_Map   *p_map;
         LV2_URID_Unmap *p_unmap;

     private:

     };
   };
}

#endif /* LV2_URID_HPP */
<commit_msg>Cleanup Test Code<commit_after>/****************************************************************************

    urid.hpp - support file for writing LV2 plugins in C++

    Copyright (C) 2012 Michael Fisher <mfisher31@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

****************************************************************************/

#ifndef LV2_URID_HPP
#define LV2_URID_HPP

#include <lv2/lv2plug.in/ns/ext/urid/urid.h>

namespace LV2 {

   /**
      The URID Extension.
      The actual type that your plugin class will inherit when you use
      this mixin is the internal struct template I.
      @ingroup pluginmixins
   */

   LV2MM_MIXIN_CLASS URID {

     LV2MM_MIXIN_DERIVED {

         I() { }

         /** @internal */
         static void map_feature_handlers(FeatureHandlerMap& hmap)
         {
            hmap[LV2_URID__map] = &I<Derived>::handle_map_feature;
            hmap[LV2_URID__unmap] = &I<Derived>::handle_unmap_feature;
         }

         /** @internal */
         static void handle_map_feature(void* instance, void* data)
         {
            Derived* d = reinterpret_cast<Derived*>(instance);
            I<Derived>* fe = static_cast<I<Derived>*>(d);

            fe->p_map = reinterpret_cast<LV2_URID_Map*>(data);
            fe->m_ok = true;
         }

         /** @internal */
         static void handle_unmap_feature(void* instance, void* data)
         {
            Derived* d = reinterpret_cast<Derived*>(instance);
            I<Derived>* fe = static_cast<I<Derived>*>(d);

            fe->p_unmap = reinterpret_cast<LV2_URID_Unmap*> (data);
            fe->m_ok = true;
         }

         bool check_ok()
         {
            if (LV2MM_DEBUG) {
              std::clog<<"    [LV2::URID] Validation "
                       <<(this->m_ok ? "succeeded" : "failed")<<"."<<std::endl;
            }
            return this->m_ok;
         }

     protected:

         /**
            Get the URI for a previously mapped numeric ID.

            Returns NULL if @p urid is not yet mapped.  Otherwise, the corresponding
            URI is returned in a canonical form.  This MAY not be the exact same
            string that was originally passed to LV2_URID_Map::map(), but it MUST be
            an identical URI according to the URI syntax specification (RFC3986).  A
            non-NULL return for a given @p urid will always be the same for the life
            of the plugin.  Plugins that intend to perform string comparison on
            unmapped URIs SHOULD first canonicalise URI strings with a call to
            map_uri() followed by a call to unmap_uri().

            @param urid The ID to be mapped back to the URI string.
         */
         const char* unmap (LV2_URID urid)
         {
            return p_unmap->unmap (p_unmap->handle, urid);
         }

         /**
            Get the numeric ID of a URI.

            If the ID does not already exist, it will be created.

            This function is referentially transparent; any number of calls with the
            same arguments is guaranteed to return the same value over the life of a
            plugin instance.  Note, however, that several URIs MAY resolve to the
            same ID if the host considers those URIs equivalent.

            This function is not necessarily very fast or RT-safe: plugins SHOULD
            cache any IDs they might need in performance critical situations.

            The return value 0 is reserved and indicates that an ID for that URI
            could not be created for whatever reason.  However, hosts SHOULD NOT
            return 0 from this function in non-exceptional circumstances (i.e. the
            URI map SHOULD be dynamic).

            @param uri The URI to be mapped to an integer ID.
         */
         LV2_URID map (const char* uri)
         {
            return p_map->map (p_map->handle, uri);
         }

         LV2_URID_Map   *p_map;
         LV2_URID_Unmap *p_unmap;

     private:

     };
   };
}

#endif /* LV2_URID_HPP */
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <boost/range/adaptor/transformed.hpp>

class T : public ::testing::Test
{
public:
    virtual ~T(){}
};

TEST_F(T, test1)
{
    EXPECT_TRUE(true);
}

TEST(F, test2)
{
    EXPECT_TRUE(true);
}
<commit_msg>fix style<commit_after>#include <boost/range/adaptor/transformed.hpp>
#include <gtest/gtest.h>

class T : public ::testing::Test
{
public:
    virtual ~T()
    {
    }
};

TEST_F(T, test1)
{
    EXPECT_TRUE(true);
}

TEST(F, test2)
{
    EXPECT_TRUE(true);
}
<|endoftext|>


<commit_before>#include "ATypeEffect.h"

ATypeEffect::ATypeEffect(CPlayer* follower)
{
	for (int i = 0; i < 3;++i)
		mExplosion[i] = NNAnimation::Create();

	wchar_t temp[256] = { 0 };
	for (int i = 0; i < 40; i++)
	{
		wsprintf(temp, L"Sprite/FireSkill/%d.png", i);

		for (int j = 0; j < 3; ++j)
			mExplosion[j]->AddFrameNode(temp);
	}

	for (int i = 0; i < 3; ++i)
		mExplosion[i]->SetFrameTimeInSection(0.05f, 0, 39);

	mFollower = follower;
	mLifeTime = mExplosion[0]->GetPlayTime() * 3;
	mDirection = mFollower->GetPlayerRotation();
	mDistance = 100.f;
	mIndex = 0;
	mExplosionTerm = 0.5f;
	mTimeCount = 0.f;

	SetPosition(mFollower->GetPlayerPosition().GetX() - 65.f, mFollower->GetPlayerPosition().GetY() - 40.f);
	mNextExplosionPoint.SetPoint(mDistance * std::cosf(mDirection), mDistance * std::sinf(mDirection));

	for (int i = 0; i < 3; ++i)
	{
		mExplosion[i]->SetLoop(false);
		mExplosion[i]->SetVisible(false);

		if (i>0)
			mExplosion[i]->SetPosition(mExplosion[i-1]->GetPositionX() + mNextExplosionPoint.GetX(), mExplosion[i-1]->GetPositionY() + mNextExplosionPoint.GetY());

		AddChild(mExplosion[i]);
	}

	mExplosion[mIndex++]->SetVisible(true);

// 	mTypeEffect = NNParticleSystem::Create(L"Sprite/FlashEffect.png");
//
// 	mTypeEffect->SetMinStartSpeed(100.f);
// 	mTypeEffect->SetMaxStartSpeed(110.f);
// 	mTypeEffect->SetMinEndSpeed(120.f);
// 	mTypeEffect->SetMaxEndSpeed(130.f);
//
// 	mTypeEffect->SetCreateParticlePerSecond(60);
// 	mTypeEffect->SetSpreadDegree(360.f);
// 	mTypeEffect->SetMinLifeTime(0.5f);
// 	mTypeEffect->SetMaxLifeTime(0.5f);
//
// 	mTypeEffect->SetMinStartRodiusX( 50.f );
// 	mTypeEffect->SetMinStartRodiusY( 50.f );
// 	mTypeEffect->SetMaxStartRodiusX( 60.f );
// 	mTypeEffect->SetMaxStartRodiusY( 60.f );
//
// 	AddChild(mTypeEffect);
}
ATypeEffect::~ATypeEffect()
{
}

void ATypeEffect::Render()
{
	IEffect::Render();
}
void ATypeEffect::Update( float dTime )
{
	IEffect::Update( dTime );

	mTimeCount += dTime;

	if (mTimeCount >= mExplosionTerm)
	{
		mTimeCount -= mExplosionTerm;
		mExplosion[mIndex++]->SetVisible(false);
		mExplosion[mIndex]->SetVisible(true);
	}
	if (mLifeTime < mNowLifeTime)
	{
		mIsEnd = true;
// 		mTypeEffect->SetCreate(false);
//
// 		if (mTypeEffect->GetCount() == 0)
//
	}
}<commit_msg># ATypeEffect<commit_after>#include "ATypeEffect.h"

ATypeEffect::ATypeEffect(CPlayer* follower)
{
	for (int i = 0; i < 3;++i)
		mExplosion[i] = NNAnimation::Create();

	wchar_t temp[256] = { 0 };
	for (int i = 0; i < 40; i++)
	{
		wsprintf(temp, L"Sprite/FireSkill/%d.png", i);

		for (int j = 0; j < 3; ++j)
			mExplosion[j]->AddFrameNode(temp);
	}

	for (int i = 0; i < 3; ++i)
		mExplosion[i]->SetFrameTimeInSection(0.02f, 0, 39);

	mFollower = follower;
	mDirection = mFollower->GetPlayerRotation();
	mDistance = 100.f;
	mIndex = 0;
	mExplosionTerm = 0.3f;
	mTimeCount = 0.f;
	mLifeTime = mExplosion[0]->GetPlayTime() + mExplosionTerm * 3;
	mLifeTime = 4.f;

	SetPosition(mFollower->GetPlayerPosition().GetX() - 65.f, mFollower->GetPlayerPosition().GetY() - 65.f);
	mNextExplosionPoint.SetPoint(mDistance * std::cosf(mDirection), mDistance * std::sinf(mDirection));

	mExplosion[0]->SetPosition(mNextExplosionPoint.GetX(), mNextExplosionPoint.GetY());
	for (int i = 0; i < 3; ++i)
	{
		mExplosion[i]->SetLoop(false);
		mExplosion[i]->SetVisible(false);

		if (i>0)
			mExplosion[i]->SetPosition(mExplosion[i-1]->GetPositionX() + mNextExplosionPoint.GetX(), mExplosion[i-1]->GetPositionY() + mNextExplosionPoint.GetY());

		AddChild(mExplosion[i]);
	}

	mExplosion[mIndex]->SetVisible(true);

// 	mTypeEffect = NNParticleSystem::Create(L"Sprite/FlashEffect.png");
//
// 	mTypeEffect->SetMinStartSpeed(100.f);
// 	mTypeEffect->SetMaxStartSpeed(110.f);
// 	mTypeEffect->SetMinEndSpeed(120.f);
// 	mTypeEffect->SetMaxEndSpeed(130.f);
//
// 	mTypeEffect->SetCreateParticlePerSecond(60);
// 	mTypeEffect->SetSpreadDegree(360.f);
// 	mTypeEffect->SetMinLifeTime(0.5f);
// 	mTypeEffect->SetMaxLifeTime(0.5f);
//
// 	mTypeEffect->SetMinStartRodiusX( 50.f );
// 	mTypeEffect->SetMinStartRodiusY( 50.f );
// 	mTypeEffect->SetMaxStartRodiusX( 60.f );
// 	mTypeEffect->SetMaxStartRodiusY( 60.f );
//
// 	AddChild(mTypeEffect);
}
ATypeEffect::~ATypeEffect()
{
}

void ATypeEffect::Render()
{
	IEffect::Render();
}
void ATypeEffect::Update( float dTime )
{
	IEffect::Update( dTime );

	mTimeCount += dTime;

	if (mIndex < 2 && mTimeCount >= mExplosionTerm)
	{
		mTimeCount -= mExplosionTerm;
		mExplosion[++mIndex]->SetVisible(true);
	}
	if (mLifeTime < mNowLifeTime)
	{
		mIsEnd = true;
// 		mTypeEffect->SetCreate(false);
//
// 		if (mTypeEffect->GetCount() == 0)
//
	}
}<|endoftext|>


<commit_before>namespace gen {

/* Internal functions only to be used from other sources in this directory. */
namespace internal {

  /* Writers preference */
  struct rw_semaphore {
    protected:
    std::mutex m{};  // protects both the members of this and the resource
    unsigned r{0}, w{0};  // active readers, waiting writers
    std::condition_variable rq{}, wq{};  // read queue, write queue
    size_t mod_cnt{0};  // modification counter

    friend class rw_lock;

    public:
    rw_semaphore() { }

    rw_semaphore(const rw_semaphore&) = delete;
    rw_semaphore(rw_semaphore&&) = delete;
    rw_semaphore& operator= (const rw_semaphore&) = delete;
    rw_semaphore& operator= (rw_semaphore&&) = delete;

    size_t get_mod_cnt() { return mod_cnt; }
  };

  class rw_lock {
    /* Invariants:
     *  mutex free ⇒ semaphore members and resource constant
     *  pers_lock free ⇒ no active writes (resource constant)
     *  w = 0 ⇒ rq notified
     *  r = 0 ⇒ wq notified
     *  write active ⇒ r = 0, w > 0, pers_lock */
    rw_semaphore& s;
    std::unique_lock<std::mutex> pers_lock{};
    bool write{false};

    protected:
    rw_lock(rw_semaphore& _s, bool _w): s(_s), write(_w) {
      if(write) {
        // Logic: write creates a persistent lock, preventing other writes from
        // starting. They can still execute ++s.w to let themselves known but
        // this enqueues them at the end of wq.wait. Write can only start when
        // everyone is finished reading.
        pers_lock = std::unique_lock<std::mutex>(s.m);
        ++s.w;
        s.wq.wait(pers_lock, [&] { return s.r == 0; });
      } else {
        // Logic: read creates a temporary lock for the purposes of rq.wait and
        // s.r access. Read can only start when no one is writing or waiting to
        // write.
        std::unique_lock<std::mutex> temp_lock(s.m);
        s.rq.wait(temp_lock, [&] { return s.w == 0; });
        ++s.r;
      }
    }

    ~rw_lock() {
      if(write) {
        --s.w;
        ++s.mod_cnt;
        pers_lock.unlock();
        s.rq.notify_all();
      } else {
        {
          std::unique_lock<std::mutex> temp_lock(s.m);
          --s.r;
        }
        s.wq.notify_all();
      }
    }

    rw_lock(const rw_lock&) = delete;
    rw_lock(rw_lock&&) = delete;
    rw_lock& operator= (const rw_lock&) = delete;
    rw_lock& operator= (rw_lock&&) = delete;

    public:
    bool upgrade() {
      if(write)
        return false;
      // Atomically combined action of -read and +write
      pers_lock = std::unique_lock<std::mutex>(s.m);
      ++s.w;
      --s.r;
      write = true;
      s.wq.wait(pers_lock, [&] { return s.r == 0; });
      // It makes no sense for another waiting write to wake up now, that
      // would defeat the purpose of the upgrade. But we need to notify the
      // other threads that r might be zero as no one else would do that.
      // Keeping pers_lock allows them to exit wait() but stay stuck at the
      // mutex lock.
      s.wq.notify_all();
      return true;
    }

    void downgrade() {
      if(!write)
        return;
      // Atomically combined action of -write and +read.
      --s.w;
      ++s.mod_cnt;
      ++s.r;
      write = false;
      pers_lock.unlock();
      // Other waiting reads can proceed now, the write is done
      s.rq.notify_all();
    }
  };

  class read_lock: public rw_lock {
    public:
    read_lock(rw_semaphore& s): rw_lock(s, false) { }
  };

  class write_lock: public rw_lock {
    public:
    write_lock(rw_semaphore& s): rw_lock(s, true) { }
  };

  class upgrade_lock {
    rw_lock& l;
    bool u{false};

    public:
    upgrade_lock(rw_lock& _l): l(_l) {
      u = l.upgrade();
    }

    ~upgrade_lock() {
      if(u)
        l.downgrade();
    }
  };

} // namespace internal

} // namespace gen
<commit_msg>RW semaphore: update mod_cnt logic and invariants<commit_after>namespace gen {

/* Internal functions only to be used from other sources in this directory. */
namespace internal {

  /* Writers preference */
  struct rw_semaphore {
    /* Invariants:
     *  mutex free ⇒ semaphore members and resource constant
     *  pers_lock free ⇒ no active writes (resource constant)
     *  w = 0 ⇔ no writers waiting
     *  r = 0 ⇔ no readers active
     *  w → 0 ⇒ rq notified
     *  r → 0 ⇒ wq notified
     *  cnt = cache_cnt ⇒ res = cache_res
     *  write active ⇒ r = 0, w > 0, pers_lock, cnt > cache_cnt
     * Corollary:
     *  see rw_lock::upgrade()! */
    protected:
    std::mutex m{};  // protects both the members of this and the resource
    unsigned r{0}, w{0};  // active readers, waiting writers
    std::condition_variable rq{}, wq{};  // read queue, write queue
    size_t mod_cnt{0};  // modification counter

    friend class rw_lock;

    public:
    rw_semaphore() { }

    rw_semaphore(const rw_semaphore&) = delete;
    rw_semaphore(rw_semaphore&&) = delete;
    rw_semaphore& operator= (const rw_semaphore&) = delete;
    rw_semaphore& operator= (rw_semaphore&&) = delete;

    size_t get_mod_cnt() { return mod_cnt; }
  };

  class rw_lock {
    rw_semaphore& s;
    std::unique_lock<std::mutex> pers_lock{};
    bool write{false};

    protected:
    rw_lock(rw_semaphore& _s, bool _w): s(_s), write(_w) {
      if(write) {
        // Logic: write creates a persistent lock, preventing other writes from
        // starting. They can still execute ++s.w to let themselves known but
        // this enqueues them at the end of wq.wait. Write can only start when
        // everyone is finished reading.
        pers_lock = std::unique_lock<std::mutex>(s.m);
        ++s.w;
        s.wq.wait(pers_lock, [&] { return s.r == 0; });
        ++s.mod_cnt;
      } else {
        // Logic: read creates a temporary lock for the purposes of rq.wait and
        // s.r access. Read can only start when no one is writing or waiting to
        // write.
        std::unique_lock<std::mutex> temp_lock(s.m);
        s.rq.wait(temp_lock, [&] { return s.w == 0; });
        ++s.r;
      }
    }

    ~rw_lock() {
      if(write) {
        --s.w;
        pers_lock.unlock();
        s.rq.notify_all();
      } else {
        {
          std::unique_lock<std::mutex> temp_lock(s.m);
          --s.r;
        }
        s.wq.notify_all();
      }
    }

    rw_lock(const rw_lock&) = delete;
    rw_lock(rw_lock&&) = delete;
    rw_lock& operator= (const rw_lock&) = delete;
    rw_lock& operator= (rw_lock&&) = delete;

    public:
    /* Important: don't rely on any information about the resource obtained
     * prior to upgrade()! Other writers may have stepped in during the wait.
     * This won't be any of the threads started with a writers' lock since
     * they are waiting for *us* to finish but if more readers simultaneously
     * want to upgrade then they will have to wait for each other.
     *
     * However, a thread can check if its upgrade request was uninterrupted
     * by other threads by comparing if s.mod_cnt raised by 1 exactly.
     *
     * This admittedly makes a lock upgrade not much different from a read
     * release followed by write acquire. The difference is that an upgraded
     * lock always has priority over waiting writes. */
    bool upgrade() {
      if(write)
        return false;
      // Atomically combined action of -read and +write
      pers_lock = std::unique_lock<std::mutex>(s.m);
      ++s.w;
      --s.r;
      write = true;
      s.wq.wait(pers_lock, [&] { return s.r == 0; });
      ++s.mod_cnt;
      // It makes no sense for another waiting write to wake up now, that
      // would defeat the purpose of the upgrade. But we need to notify the
      // other threads that r might be zero as no one else would do that.
      // Keeping pers_lock allows them to exit wait() but stay stuck at the
      // mutex lock.
      s.wq.notify_all();
      return true;
    }

    /* Downgrade a write lock to a read lock, allowing the read to finish
     * before another writer obtains the lock. */
    void downgrade() {
      if(!write)
        return;
      // Atomically combined action of -write and +read.
      --s.w;
      ++s.r;
      write = false;
      pers_lock.unlock();
      // Other waiting reads can proceed now, the write is done
      s.rq.notify_all();
    }
  };

  class read_lock: public rw_lock {
    public:
    read_lock(rw_semaphore& s): rw_lock(s, false) { }
  };

  class write_lock: public rw_lock {
    public:
    write_lock(rw_semaphore& s): rw_lock(s, true) { }
  };

  class upgrade_lock {
    rw_lock& l;
    bool u{false};

    public:
    upgrade_lock(rw_lock& _l): l(_l) {
      u = l.upgrade();
    }

    ~upgrade_lock() {
      if(u)
        l.downgrade();
    }
  };

} // namespace internal

} // namespace gen
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "../Characters/Format.h"
#include    "../Characters/CString/Utilities.h"
#include    "../Characters/StringBuilder.h"
#include    "../Characters/ToString.h"
#include    "../Memory/BlockAllocated.h"

#include    "Common.h"
#include    "Sleep.h"
#include    "TimeOutException.h"

#include    "ThreadPool.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;

using   Characters::String;
using   Characters::StringBuilder;

class   ThreadPool::MyRunnable_  {
public:
    MyRunnable_ (ThreadPool& threadPool)
        : fCurTaskUpdateCritSection_ ()
        , fThreadPool_ (threadPool)
        , fCurTask_ ()
        , fNextTask_ ()
    {
    }

public:
    ThreadPool::TaskType    GetCurrentTask () const
    {
        auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
        // THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
        // Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
        Assert (fCurTask_ == nullptr or fNextTask_ == nullptr);   // one or both must be null
        return fCurTask_ == nullptr ? fNextTask_ : fCurTask_;
    }

public:
    nonvirtual void    Run ()
    {
        // For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

        // Keep grabbing new tasks, and running them
        while (true) {
            {
                fThreadPool_.WaitForNextTask_ (&fNextTask_);            // This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                Assert (fNextTask_ != nullptr);
                Assert (fCurTask_ == nullptr);
                fCurTask_ = fNextTask_;
                fNextTask_ = nullptr;
                Assert (fCurTask_ != nullptr);
                Assert (fNextTask_ == nullptr);
            }
            try {
                fCurTask_ ();
                fCurTask_ = nullptr;
            }
            catch (const Thread::AbortException&) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                throw;  // cancel this thread
            }
            catch (...) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                // other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
            }
        }
    }

private:
    mutable recursive_mutex     fCurTaskUpdateCritSection_;
    ThreadPool&                 fThreadPool_;
    ThreadPool::TaskType        fCurTask_;
    ThreadPool::TaskType        fNextTask_;
};

/*
 ********************************************************************************
 ****************************** Execution::ThreadPool ***************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
    : fCriticalSection_ ()
    , fAborted_ (false)
    , fThreads_ ()
    , fPendingTasks_ ()
    , fTasksMaybeAdded_ (WaitableEvent::eAutoReset)
{
    SetPoolSize (nThreads);
}

ThreadPool::~ThreadPool ()
{
    try {
        this->AbortAndWaitForDone ();
    }
    catch (...) {
        DbgTrace ("Ignore exception in destroying thread pool ** probably bad thing...");
    }
}

unsigned int    ThreadPool::GetPoolSize () const
{
    return static_cast<unsigned int> (fThreads_.size ());
}

void    ThreadPool::SetPoolSize (unsigned int poolSize)
{
    Debug::TraceContextBumper ctx ("ThreadPool::SetPoolSize");
    Require (not fAborted_);
    auto    critSec { make_unique_lock (fCriticalSection_) };
    while (poolSize > fThreads_.size ()) {
        fThreads_.Add (mkThread_ ());
    }

    // Still quite weak implementation of REMOVAL
    while (poolSize < fThreads_.size ()) {
        // iterate over threads if any not busy, remove that one
        bool anyFoundToKill = false;
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (ct == nullptr) {
                // since we have fCriticalSection_ - we can safely remove this thread
                fThreads_.Remove (i);
                anyFoundToKill = true;
                break;
            }
        }
        if (not anyFoundToKill) {
            // @todo - fix this better/eventually
            DbgTrace ("Failed to lower the loop size - cuz all threads busy - giving up");
            return;
        }
    }
}

ThreadPool::TaskType    ThreadPool::AddTask (const TaskType& task)
{
    //Debug::TraceContextBumper ctx ("ThreadPool::AddTask");
    Require (not fAborted_);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.push_back (task);
    }

    // Notify any waiting threads to wakeup and claim the next task
    fTasksMaybeAdded_.Set ();
    // this would be a race - if aborting and adding tasks at the same time
    Require (not fAborted_);
    return task;
}

void    ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTask");
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                fPendingTasks_.erase (i);
                return;
            }
        }
    }

    // If we got here - its NOT in the task Q, so maybe its already running.
    //
    //

    // TODO:
    //      We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
    //      But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
    //      actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for something
    //      quite rare.
    //
    //      Anyhow SB OK for now to just not allow aborting a task which has already started....
    Thread  thread2Kill;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (task == ct) {
                thread2Kill =   i->fThread;
                fThreads_.Update (i, mkThread_ ());
                break;
            }
        }
    }
    if (thread2Kill.GetStatus () != Thread::Status::eNull) {
        thread2Kill.AbortAndWaitForDone (timeout);
    }
}

void    ThreadPool::AbortTasks (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTasks");
    auto tps = GetPoolSize ();
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            // @todo fix wrong timeout value here
            ti.fThread.AbortAndWaitForDone (timeout);
        }
        fThreads_.RemoveAll ();
    }
    // hack - not a good design or impl!!! - waste to recreate if not needed!
    SetPoolSize (tps);
}

bool    ThreadPool::IsPresent (const TaskType& task) const
{
    Require (task != nullptr);
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                return true;
            }
        }
    }
    return IsRunning (task);
}

bool    ThreadPool::IsRunning (const TaskType& task) const
{
    Require (task != nullptr);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    rTask   { tr->GetCurrentTask () };
            if (task == rTask) {
                return true;
            }
        }
    }
    return false;
}

void    ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForTask");
    // Inefficient / VERY SLOPPY impl
    using   Time::DurationSecondsType;
    DurationSecondsType   timeoutAt   =   timeout + Time::GetTickCount ();
    while (true) {
        if (not IsPresent (task)) {
            return;
        }
        DurationSecondsType   remaining   =   timeoutAt - Time::GetTickCount ();
        Execution::ThrowTimeoutExceptionAfter (timeoutAt);
        Execution::Sleep (min<DurationSecondsType> (remaining, 1.0));
    }
}

Collection<ThreadPool::TaskType>    ThreadPool::GetTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        result.AddAll (fPendingTasks_.begin (), fPendingTasks_.end ());          // copy pending tasks
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

Collection<ThreadPool::TaskType>    ThreadPool::GetRunningTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

size_t  ThreadPool::GetTasksCount () const
{
    size_t  count   =   0;
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                count++;
            }
        }
    }
    return count;
}

size_t   ThreadPool::GetPendingTasksCount () const
{
    size_t  count   =   0;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
    }
    return count;
}

void    ThreadPool::WaitForDoneUntil (Time::DurationSecondsType timeoutAt) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForDoneUntil");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Require (fAborted_);
    {
        Collection<Thread>  threadsToShutdown;      // cannot keep critical section while waiting on subthreads since they may need to acquire the critsection for whatever they are doing...
#if     qDefaultTracingOn
        Collection<String>  threadsNotAlreadyDone;  // just for DbgTrace message purposes
#endif
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            for (auto ti : fThreads_) {
                threadsToShutdown.Add (ti.fThread);
#if     qDefaultTracingOn
                if (not ti.fThread.IsDone ()) {
                    threadsNotAlreadyDone.Add (Execution::FormatThreadID (ti.fThread.GetID ()));
                }
#endif
            }
        }
        DbgTrace (L"threadsToShutdown.size = %d", threadsToShutdown.size ());
#if     qDefaultTracingOn
        DbgTrace (L"threadsNotAlreadyDone = %s", Characters::ToString (threadsNotAlreadyDone).c_str ());
#endif
        for (auto t : threadsToShutdown) {
            t.WaitForDoneUntil (timeoutAt);
        }
    }
}

void    ThreadPool::Abort ()
{
    Debug::TraceContextBumper ctx ("ThreadPool::Abort");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    fAborted_ = true;   // No race, because fAborted never 'unset'
    // no need to set fTasksMaybeAdded_, since aborting each thread should be sufficient
    {
        // Clear the task Q and then abort each thread
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
        for (TPInfo_ && ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
}

void    ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper traceCtx ("ThreadPool::AbortAndWaitForDone");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Thread::SuppressInterruptionInContext ctx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    Abort ();
    WaitForDone (timeout);
}

String  ThreadPool::ToString () const
{
    auto    critSec { make_unique_lock (fCriticalSection_) };
    StringBuilder   sb;
    sb += L"{";
    sb += Characters::Format (L"pending-task-count: %d", GetPendingTasksCount ()) + L", ";
    sb += Characters::Format (L"running-task-count: %d", GetRunningTasks ().size ()) + L", ";
    sb += Characters::Format (L"pool-thread-count: %d", fThreads_.size ()) + L", ";
    sb += L"}";
    return sb.str ();
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void    ThreadPool::WaitForNextTask_ (TaskType* result)
{
    RequireNotNull (result);
    if (fAborted_) {
        Execution::Throw (Thread::AbortException ());
    }

    while (true) {
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            if (not fPendingTasks_.empty ()) {
                *result =   fPendingTasks_.front ();
                fPendingTasks_.pop_front ();
                DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
                return;
            }
        }

        // Prevent spinwaiting... This event is SET when any new item arrives
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
        fTasksMaybeAdded_.Wait ();
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
    }
}

ThreadPool::TPInfo_      ThreadPool::mkThread_ ()
{
    shared_ptr<MyRunnable_> r   { make_shared<ThreadPool::MyRunnable_> (*this) };
    Thread                  t   { [r] () { r->Run (); } };
    static  int sThreadNum_ =   1;  // race condition for updating this number, but who cares - its purely cosmetic...
    t.SetThreadName (Characters::CString::Format (L"Thread Pool Entry %d", sThreadNum_++));
    t.Start ();
    return TPInfo_ { t, r };
}
<commit_msg>moved Thread::SuppressInterruptionInContext in ThreadPool::Abort ()<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "../Characters/Format.h"
#include    "../Characters/CString/Utilities.h"
#include    "../Characters/StringBuilder.h"
#include    "../Characters/ToString.h"
#include    "../Memory/BlockAllocated.h"

#include    "Common.h"
#include    "Sleep.h"
#include    "TimeOutException.h"

#include    "ThreadPool.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;

using   Characters::String;
using   Characters::StringBuilder;

class   ThreadPool::MyRunnable_  {
public:
    MyRunnable_ (ThreadPool& threadPool)
        : fCurTaskUpdateCritSection_ ()
        , fThreadPool_ (threadPool)
        , fCurTask_ ()
        , fNextTask_ ()
    {
    }

public:
    ThreadPool::TaskType    GetCurrentTask () const
    {
        auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
        // THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
        // Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
        Assert (fCurTask_ == nullptr or fNextTask_ == nullptr);   // one or both must be null
        return fCurTask_ == nullptr ? fNextTask_ : fCurTask_;
    }

public:
    nonvirtual void    Run ()
    {
        // For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

        // Keep grabbing new tasks, and running them
        while (true) {
            {
                fThreadPool_.WaitForNextTask_ (&fNextTask_);            // This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                Assert (fNextTask_ != nullptr);
                Assert (fCurTask_ == nullptr);
                fCurTask_ = fNextTask_;
                fNextTask_ = nullptr;
                Assert (fCurTask_ != nullptr);
                Assert (fNextTask_ == nullptr);
            }
            try {
                fCurTask_ ();
                fCurTask_ = nullptr;
            }
            catch (const Thread::AbortException&) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                throw;  // cancel this thread
            }
            catch (...) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                // other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
            }
        }
    }

private:
    mutable recursive_mutex     fCurTaskUpdateCritSection_;
    ThreadPool&                 fThreadPool_;
    ThreadPool::TaskType        fCurTask_;
    ThreadPool::TaskType        fNextTask_;
};

/*
 ********************************************************************************
 ****************************** Execution::ThreadPool ***************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
    : fCriticalSection_ ()
    , fAborted_ (false)
    , fThreads_ ()
    , fPendingTasks_ ()
    , fTasksMaybeAdded_ (WaitableEvent::eAutoReset)
{
    SetPoolSize (nThreads);
}

ThreadPool::~ThreadPool ()
{
    try {
        this->AbortAndWaitForDone ();
    }
    catch (...) {
        DbgTrace ("Ignore exception in destroying thread pool ** probably bad thing...");
    }
}

unsigned int    ThreadPool::GetPoolSize () const
{
    return static_cast<unsigned int> (fThreads_.size ());
}

void    ThreadPool::SetPoolSize (unsigned int poolSize)
{
    Debug::TraceContextBumper ctx ("ThreadPool::SetPoolSize");
    Require (not fAborted_);
    auto    critSec { make_unique_lock (fCriticalSection_) };
    while (poolSize > fThreads_.size ()) {
        fThreads_.Add (mkThread_ ());
    }

    // Still quite weak implementation of REMOVAL
    while (poolSize < fThreads_.size ()) {
        // iterate over threads if any not busy, remove that one
        bool anyFoundToKill = false;
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (ct == nullptr) {
                // since we have fCriticalSection_ - we can safely remove this thread
                fThreads_.Remove (i);
                anyFoundToKill = true;
                break;
            }
        }
        if (not anyFoundToKill) {
            // @todo - fix this better/eventually
            DbgTrace ("Failed to lower the loop size - cuz all threads busy - giving up");
            return;
        }
    }
}

ThreadPool::TaskType    ThreadPool::AddTask (const TaskType& task)
{
    //Debug::TraceContextBumper ctx ("ThreadPool::AddTask");
    Require (not fAborted_);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.push_back (task);
    }

    // Notify any waiting threads to wakeup and claim the next task
    fTasksMaybeAdded_.Set ();
    // this would be a race - if aborting and adding tasks at the same time
    Require (not fAborted_);
    return task;
}

void    ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTask");
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                fPendingTasks_.erase (i);
                return;
            }
        }
    }

    // If we got here - its NOT in the task Q, so maybe its already running.
    //
    //

    // TODO:
    //      We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
    //      But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
    //      actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for something
    //      quite rare.
    //
    //      Anyhow SB OK for now to just not allow aborting a task which has already started....
    Thread  thread2Kill;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (task == ct) {
                thread2Kill =   i->fThread;
                fThreads_.Update (i, mkThread_ ());
                break;
            }
        }
    }
    if (thread2Kill.GetStatus () != Thread::Status::eNull) {
        thread2Kill.AbortAndWaitForDone (timeout);
    }
}

void    ThreadPool::AbortTasks (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTasks");
    auto tps = GetPoolSize ();
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            // @todo fix wrong timeout value here
            ti.fThread.AbortAndWaitForDone (timeout);
        }
        fThreads_.RemoveAll ();
    }
    // hack - not a good design or impl!!! - waste to recreate if not needed!
    SetPoolSize (tps);
}

bool    ThreadPool::IsPresent (const TaskType& task) const
{
    Require (task != nullptr);
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                return true;
            }
        }
    }
    return IsRunning (task);
}

bool    ThreadPool::IsRunning (const TaskType& task) const
{
    Require (task != nullptr);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    rTask   { tr->GetCurrentTask () };
            if (task == rTask) {
                return true;
            }
        }
    }
    return false;
}

void    ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForTask");
    // Inefficient / VERY SLOPPY impl
    using   Time::DurationSecondsType;
    DurationSecondsType   timeoutAt   =   timeout + Time::GetTickCount ();
    while (true) {
        if (not IsPresent (task)) {
            return;
        }
        DurationSecondsType   remaining   =   timeoutAt - Time::GetTickCount ();
        Execution::ThrowTimeoutExceptionAfter (timeoutAt);
        Execution::Sleep (min<DurationSecondsType> (remaining, 1.0));
    }
}

Collection<ThreadPool::TaskType>    ThreadPool::GetTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        result.AddAll (fPendingTasks_.begin (), fPendingTasks_.end ());          // copy pending tasks
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

Collection<ThreadPool::TaskType>    ThreadPool::GetRunningTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

size_t  ThreadPool::GetTasksCount () const
{
    size_t  count   =   0;
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                count++;
            }
        }
    }
    return count;
}

size_t   ThreadPool::GetPendingTasksCount () const
{
    size_t  count   =   0;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
    }
    return count;
}

void    ThreadPool::WaitForDoneUntil (Time::DurationSecondsType timeoutAt) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForDoneUntil");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Require (fAborted_);
    {
        Collection<Thread>  threadsToShutdown;      // cannot keep critical section while waiting on subthreads since they may need to acquire the critsection for whatever they are doing...
#if     qDefaultTracingOn
        Collection<String>  threadsNotAlreadyDone;  // just for DbgTrace message purposes
#endif
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            for (auto ti : fThreads_) {
                threadsToShutdown.Add (ti.fThread);
#if     qDefaultTracingOn
                if (not ti.fThread.IsDone ()) {
                    threadsNotAlreadyDone.Add (Execution::FormatThreadID (ti.fThread.GetID ()));
                }
#endif
            }
        }
        DbgTrace (L"threadsToShutdown.size = %d", threadsToShutdown.size ());
#if     qDefaultTracingOn
        DbgTrace (L"threadsNotAlreadyDone = %s", Characters::ToString (threadsNotAlreadyDone).c_str ());
#endif
        for (auto t : threadsToShutdown) {
            t.WaitForDoneUntil (timeoutAt);
        }
    }
}

void    ThreadPool::Abort ()
{
    Debug::TraceContextBumper ctx ("ThreadPool::Abort");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    fAborted_ = true;   // No race, because fAborted never 'unset'
    // no need to set fTasksMaybeAdded_, since aborting each thread should be sufficient
    {
        // Clear the task Q and then abort each thread
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
        for (TPInfo_ && ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
}

void    ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper traceCtx ("ThreadPool::AbortAndWaitForDone");
    Thread::SuppressInterruptionInContext ctx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Abort ();
    WaitForDone (timeout);
}

String  ThreadPool::ToString () const
{
    auto    critSec { make_unique_lock (fCriticalSection_) };
    StringBuilder   sb;
    sb += L"{";
    sb += Characters::Format (L"pending-task-count: %d", GetPendingTasksCount ()) + L", ";
    sb += Characters::Format (L"running-task-count: %d", GetRunningTasks ().size ()) + L", ";
    sb += Characters::Format (L"pool-thread-count: %d", fThreads_.size ()) + L", ";
    sb += L"}";
    return sb.str ();
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void    ThreadPool::WaitForNextTask_ (TaskType* result)
{
    RequireNotNull (result);
    if (fAborted_) {
        Execution::Throw (Thread::AbortException ());
    }

    while (true) {
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            if (not fPendingTasks_.empty ()) {
                *result =   fPendingTasks_.front ();
                fPendingTasks_.pop_front ();
                DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
                return;
            }
        }

        // Prevent spinwaiting... This event is SET when any new item arrives
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
        fTasksMaybeAdded_.Wait ();
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
    }
}

ThreadPool::TPInfo_      ThreadPool::mkThread_ ()
{
    shared_ptr<MyRunnable_> r   { make_shared<ThreadPool::MyRunnable_> (*this) };
    Thread                  t   { [r] () { r->Run (); } };
    static  int sThreadNum_ =   1;  // race condition for updating this number, but who cares - its purely cosmetic...
    t.SetThreadName (Characters::CString::Format (L"Thread Pool Entry %d", sThreadNum_++));
    t.Start ();
    return TPInfo_ { t, r };
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2014-2015 libbitcoin developers (see COPYING).
//
//        GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY
//
///////////////////////////////////////////////////////////////////////////////
#ifndef MVS_VERSION_HPP
#define MVS_VERSION_HPP

/**
 * The semantic version of this repository as: [major].[minor].[patch]
 * For interpretation of the versioning scheme see: http://semver.org
 */

#define MVS_VERSION "0.7.3"
#define MVS_MAJOR_VERSION 0
#define MVS_MINOR_VERSION 7
#define MVS_PATCH_VERSION 3

#endif
<commit_msg>修改版本号<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2014-2015 libbitcoin developers (see COPYING).
//
//        GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY
//
///////////////////////////////////////////////////////////////////////////////
#ifndef MVS_VERSION_HPP
#define MVS_VERSION_HPP

/**
 * The semantic version of this repository as: [major].[minor].[patch]
 * For interpretation of the versioning scheme see: http://semver.org
 */

#define MVS_VERSION "0.7.4"
#define MVS_MAJOR_VERSION 0
#define MVS_MINOR_VERSION 7
#define MVS_PATCH_VERSION 4

#endif
<|endoftext|>


<commit_before>#pragma once

#include <microscopes/common/macros.hpp>
#include <microscopes/common/typedefs.hpp>
#include <microscopes/common/util.hpp>
#include <microscopes/common/variadic/dataview.hpp>

namespace microscopes{
namespace hdp_hmm{

} // namespace hdp_hmm
} // namespace microscopes<commit_msg>Started fast-and-simple HDP-HMM implementation<commit_after>#pragma once

namespace microscopes{
namespace hdp_hmm{

typedef std::vector<std::vector<size_t>> data_type; // shorten type
// To start with, just build an HDP-HMM for discrete distributions with Dirichlet prior on observations
class hdm_hmm {
public:
	hdp_hmm(size_t alphalen, data_type data) :
		alphalen_(alphalen),
		gamma_(),
		alpha0_(),
		obs_hp_(alphalen, 1.0 / alphalen)
		data_(data)
	{}
protected:
	// hyperparameters
	const size_t alphalen_; // alphabet size
	float gamma_; // top-level concentration parameter
	float alpha0_; // lower-level concentration parameter
	std::vector<double> obs_hp_; // vector of Dirichlet distribution hyperparameters for prior on observation distribution

	// data
	const data_type data_;

	// parameters updated by Gibbs sampling
	data_type assignments_; // assignment of every time step in the data to a latent state
	std::map<std::pair<size_t, size_t>, size_t> table_counts_; // (dish_id, restaurant_id) maps to number of tables in that restaurant that serve that dish
	std::vector<double> betas_; // stick lengths for upper level Dirichlet Process sample
};

} // namespace hdp_hmm
} // namespace microscopes<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_ENDIAN_HPP
#define TAO_PEGTL_INTERNAL_ENDIAN_HPP

#include <cstdint>
#include <cstring>

#include "../config.hpp"

#if defined( _WIN32 ) && !defined( __MINGW32__ )
#include "endian_win.hpp"
#else
#include "endian_gcc.hpp"
#endif

namespace TAO_PEGTL_NAMESPACE::internal
{
   template< typename N >
   [[nodiscard]] N h_to_be( const N n ) noexcept
   {
      return N( to_and_from_be< sizeof( N ) >::convert( n ) );
   }

   template< typename N >
   [[nodiscard]] N be_to_h( const N n ) noexcept
   {
      return h_to_be( n );
   }

   template< typename N >
   [[nodiscard]] N be_to_h( const void* p ) noexcept
   {
      N n;
      std::memcpy( &n, p, sizeof( n ) );
      return internal::be_to_h( n );
   }

   template< typename N >
   [[nodiscard]] N h_to_le( const N n ) noexcept
   {
      return N( to_and_from_le< sizeof( N ) >::convert( n ) );
   }

   template< typename N >
   [[nodiscard]] N le_to_h( const N n ) noexcept
   {
      return h_to_le( n );
   }

   template< typename N >
   [[nodiscard]] N le_to_h( const void* p ) noexcept
   {
      N n;
      std::memcpy( &n, p, sizeof( n ) );
      return internal::le_to_h( n );
   }

}  // namespace TAO_PEGTL_NAMESPACE::internal

#endif
<commit_msg>Fix Cygwin build<commit_after>// Copyright (c) 2017-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_ENDIAN_HPP
#define TAO_PEGTL_INTERNAL_ENDIAN_HPP

#include <cstdint>
#include <cstring>

#include "../config.hpp"

#if defined( _WIN32 ) && !defined( __MINGW32__ ) && !defined( __CYGWIN__ )
#include "endian_win.hpp"
#else
#include "endian_gcc.hpp"
#endif

namespace TAO_PEGTL_NAMESPACE::internal
{
   template< typename N >
   [[nodiscard]] N h_to_be( const N n ) noexcept
   {
      return N( to_and_from_be< sizeof( N ) >::convert( n ) );
   }

   template< typename N >
   [[nodiscard]] N be_to_h( const N n ) noexcept
   {
      return h_to_be( n );
   }

   template< typename N >
   [[nodiscard]] N be_to_h( const void* p ) noexcept
   {
      N n;
      std::memcpy( &n, p, sizeof( n ) );
      return internal::be_to_h( n );
   }

   template< typename N >
   [[nodiscard]] N h_to_le( const N n ) noexcept
   {
      return N( to_and_from_le< sizeof( N ) >::convert( n ) );
   }

   template< typename N >
   [[nodiscard]] N le_to_h( const N n ) noexcept
   {
      return h_to_le( n );
   }

   template< typename N >
   [[nodiscard]] N le_to_h( const void* p ) noexcept
   {
      N n;
      std::memcpy( &n, p, sizeof( n ) );
      return internal::le_to_h( n );
   }

}  // namespace TAO_PEGTL_NAMESPACE::internal

#endif
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkTransformFileReader_hxx
#define __itkTransformFileReader_hxx

#include "itkTransformFileReader.h"
#include "itkTransformIOFactory.h"
#include "itkCompositeTransformIOHelper.h"

namespace itk
{

template<typename ScalarType>
TransformFileReaderTemplate<ScalarType>
::TransformFileReaderTemplate() :
  m_FileName("") /* to be removed soon. See .h */
{
}

template<typename ScalarType>
TransformFileReaderTemplate<ScalarType>
::~TransformFileReaderTemplate()
{
}

template<typename ScalarType>
void TransformFileReaderTemplate<ScalarType>
::Update()
{
  if ( m_FileName == "" )
    {
    itkExceptionMacro ("No file name given");
    }

  if( m_TransformIO.IsNull() )
    {
    typedef TransformIOFactoryTemplate< ScalarType > TransformFactoryIOType;
    m_TransformIO = TransformFactoryIOType::CreateTransformIO( m_FileName.c_str(), /*TransformIOFactoryTemplate<ScalarType>::*/ ReadMode );
    if ( m_TransformIO.IsNull() )
      {
      itkExceptionMacro("Can't Create IO object for file " << m_FileName);
      }
    }

  m_TransformIO->SetFileName(m_FileName);
  m_TransformIO->Read();

  typename TransformIOType::TransformListType &ioTransformList =
  m_TransformIO->GetTransformList();

    // In the case where the first transform in the list is a
    // CompositeTransform, add all the transforms to that first
    // transform. and return a single composite item on the
    // m_TransformList
  const std::string firstTransformName = ioTransformList.front()->GetNameOfClass();
  if(firstTransformName.find("CompositeTransform") != std::string::npos)
    {
    typename TransformListType::const_iterator tit = ioTransformList.begin();
    typename TransformType::Pointer composite = (*tit).GetPointer();
      //
      // CompositeTransformIOHelperTemplate knows how to assign to the composite
      // transform's internal list
    CompositeTransformIOHelperTemplate<ScalarType> helper;
    helper.SetTransformList(composite.GetPointer(),ioTransformList);

    this->m_TransformList.push_back( composite.GetPointer() );
    }
  else  //Just return the entire list of elements
    {
    for ( typename TransformListType::iterator it =
         ioTransformList.begin();
         it != ioTransformList.end(); ++it )
      {
      this->m_TransformList.push_back( (*it).GetPointer() );
      }
    }
}

template<typename ScalarType>
void TransformFileReaderTemplate<ScalarType>
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "FileName: " << m_FileName << std::endl;
}

} // namespace itk

#endif
<commit_msg>BUG: TransformFileReader does not clear its TransformList.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkTransformFileReader_hxx
#define __itkTransformFileReader_hxx

#include "itkTransformFileReader.h"
#include "itkTransformIOFactory.h"
#include "itkCompositeTransformIOHelper.h"

namespace itk
{

template<typename ScalarType>
TransformFileReaderTemplate<ScalarType>
::TransformFileReaderTemplate() :
  m_FileName("") /* to be removed soon. See .h */
{
}

template<typename ScalarType>
TransformFileReaderTemplate<ScalarType>
::~TransformFileReaderTemplate()
{
}

template<typename ScalarType>
void TransformFileReaderTemplate<ScalarType>
::Update()
{
  if ( m_FileName == "" )
    {
    itkExceptionMacro ("No file name given");
    }

  if( m_TransformIO.IsNull() )
    {
    typedef TransformIOFactoryTemplate< ScalarType > TransformFactoryIOType;
    m_TransformIO = TransformFactoryIOType::CreateTransformIO( m_FileName.c_str(), /*TransformIOFactoryTemplate<ScalarType>::*/ ReadMode );
    if ( m_TransformIO.IsNull() )
      {
      itkExceptionMacro("Can't Create IO object for file " << m_FileName);
      }
    }

  m_TransformIO->SetFileName(m_FileName);
  m_TransformIO->Read();

  typename TransformIOType::TransformListType & ioTransformList = m_TransformIO->GetTransformList();

  // Clear old results.
  this->m_TransformList.clear();

  // In the case where the first transform in the list is a
  // CompositeTransform, add all the transforms to that first
  // transform. and return a single composite item on the
  // m_TransformList
  const std::string firstTransformName = ioTransformList.front()->GetNameOfClass();
  if(firstTransformName.find("CompositeTransform") != std::string::npos)
    {
    typename TransformListType::const_iterator tit = ioTransformList.begin();
    typename TransformType::Pointer composite = (*tit).GetPointer();

    // CompositeTransformIOHelperTemplate knows how to assign to the composite
    // transform's internal list
    CompositeTransformIOHelperTemplate<ScalarType> helper;
    helper.SetTransformList(composite.GetPointer(),ioTransformList);

    this->m_TransformList.push_back( composite.GetPointer() );
    }
  else  //Just return the entire list of elements
    {
    for ( typename TransformListType::iterator it =
         ioTransformList.begin();
         it != ioTransformList.end(); ++it )
      {
      this->m_TransformList.push_back( (*it).GetPointer() );
      }
    }
}

template<typename ScalarType>
void TransformFileReaderTemplate<ScalarType>
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "FileName: " << m_FileName << std::endl;
}

} // namespace itk

#endif
<|endoftext|>