shushus

Спринт 15. C++ Protobuf API.

May 31st, 2023
365
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.02 KB | None | 0 0
  1. -----------------------------------------------------------------------
  2. CMakeLists.txt
  3. -----------------------------------------------------------------------
  4. cmake_minimum_required(VERSION 3.10)
  5.  
  6. project(PhoneBook CXX)
  7. set(CMAKE_CXX_STANDARD 17)
  8.  
  9. find_package(Protobuf REQUIRED)
  10. find_package(Threads REQUIRED)
  11.  
  12. protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS contact.proto)
  13.  
  14. set(PHONEBOOK_FILES iterator_range.h
  15.                     main.cpp
  16.                     phone_book.h
  17.                     phone_book.cpp
  18.                     contact.proto)
  19.  
  20. add_executable(phonebook ${PROTO_SRCS}
  21.                          ${PROTO_HDRS}
  22.                          ${PHONEBOOK_FILES})
  23.                          
  24. target_include_directories(phonebook PUBLIC ${Protobuf_INCLUDE_DIRS})
  25. target_include_directories(phonebook PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
  26.  
  27. string(REPLACE "protobuf.lib" "protobufd.lib" "Protobuf_LIBRARY_DEBUG" "${Protobuf_LIBRARY_DEBUG}")
  28. string(REPLACE "protobuf.a" "protobufd.a" "Protobuf_LIBRARY_DEBUG" "${Protobuf_LIBRARY_DEBUG}")
  29.  
  30. target_link_libraries(phonebook "$<IF:$<CONFIG:Debug>,${Protobuf_LIBRARY_DEBUG},${Protobuf_LIBRARY}>" Threads::Threads)
  31. -----------------------------------------------------------------------
  32. contact.proto
  33. -----------------------------------------------------------------------
  34. syntax = "proto3";
  35.  
  36. package phone_book_serialize;
  37.  
  38. message Date {
  39.     uint32 year = 1;
  40.     uint32 month = 2;
  41.     uint32 day = 3;
  42. }
  43.  
  44. message Contact {
  45.     string name = 1;
  46.     Date birthday = 2;
  47.     repeated string phone_number = 3;
  48. }
  49.  
  50. message ContactList {
  51.     repeated Contact contact = 1;
  52. }
  53. -----------------------------------------------------------------------
  54. iterator_range.h
  55. -----------------------------------------------------------------------
  56. #pragma once
  57.  
  58. #include <algorithm>
  59.  
  60. template <typename It>
  61. class IteratorRange {
  62. public:
  63.  
  64.     IteratorRange(It first_, It last_) : first_(first_)
  65.                                        , last_(last_) {}
  66.  
  67.     It begin() const {return first_;}
  68.     It end() const {return last_;}
  69.     size_t size() const {return last_ - first_;}
  70.  
  71. private:
  72.     It first_, last_;
  73. };
  74.  
  75. template <typename Container>
  76. auto Head(Container& c, int top) {
  77.     return IteratorRange(std::begin(c), std::begin(c) + std::min<size_t>(std::max(top, 0), c.size()));
  78. }
  79. -----------------------------------------------------------------------
  80. phone_book.h
  81. -----------------------------------------------------------------------
  82. #pragma once
  83.  
  84. #include "iterator_range.h"
  85.  
  86. #include <optional>
  87. #include <sstream>
  88. #pragma once
  89.  
  90. #include "iterator_range.h"
  91.  
  92. #include <string>
  93. #include <string_view>
  94. #include <vector>
  95.  
  96. #include <contact.pb.h>
  97.  
  98. struct Date {int year, month, day;};
  99.  
  100. struct Contact {
  101.     std::string name;
  102.     std::optional<Date> birthday;
  103.     std::vector<std::string> phones;
  104. };
  105.  
  106. class PhoneBook {
  107. public:
  108.     explicit PhoneBook(std::vector<Contact> contacts);
  109.  
  110.     using Iterator = std::vector<Contact>::iterator;;
  111.     using ContactRange = IteratorRange<Iterator>;
  112.  
  113.     ContactRange FindByNamePrefix(std::string_view name_prefix) const;
  114.     void SaveTo(std::ostream& output) const;
  115.    
  116. private:
  117.     mutable std::vector<Contact> contacts_;
  118. };
  119.  
  120. PhoneBook DeserializePhoneBook(std::istream& input);
  121. -----------------------------------------------------------------------
  122. phone_book.cpp
  123. -----------------------------------------------------------------------
  124. #include "phone_book.h"
  125.  
  126. using namespace std;
  127.  
  128.  
  129. PhoneBook::PhoneBook(std::vector<Contact> contacts) : contacts_(contacts) {};
  130.  
  131. PhoneBook::ContactRange PhoneBook::FindByNamePrefix(std::string_view name_prefix) const {
  132.  
  133.     std::sort(contacts_.begin(),
  134.               contacts_.end(), [](const Contact& lhs, const Contact& rhs) {
  135.                                   return lhs.name < rhs.name;
  136.                                 });
  137.    
  138.     if (name_prefix.size() == 0) {
  139.         return {contacts_.begin(), contacts_.end()};
  140.        
  141.     } else {
  142.        
  143.         Iterator start = std::lower_bound(contacts_.begin(),
  144.                                          contacts_.end(),
  145.                                           name_prefix, [](const Contact& contact, std::string_view prefix) {
  146.                                                           return prefix > contact.name;
  147.                                                        });
  148.        
  149.         Iterator end = std::upper_bound(start,
  150.                                         contacts_.end(),
  151.                                         name_prefix, [](std::string_view prefix, const Contact& contact) {
  152.                                                         int helper = contact.name.compare(0, prefix.size(), prefix);
  153.                                                         if (helper == 0) {
  154.                                                             return false;
  155.                                                            
  156.                                                         } else {
  157.                                                             return prefix < contact.name;
  158.                                                         }
  159.                                                      });
  160.         return {start, end};
  161.     }  
  162. };
  163.  
  164. void PhoneBook::SaveTo(std::ostream& output) const {
  165.     phone_book_serialize::ContactList contact_list;
  166.    
  167.     for (const Contact contact : contacts_) {
  168.        
  169.         phone_book_serialize::Contact contact_from_phone_book;
  170.         contact_from_phone_book.set_name(contact.name);
  171.        
  172.         if (contact.birthday.has_value()) {        
  173.             contact_from_phone_book.mutable_birthday()->set_day(contact.birthday->day);
  174.             contact_from_phone_book.mutable_birthday()->set_month(contact.birthday->month);
  175.             contact_from_phone_book.mutable_birthday()->set_year(contact.birthday->year);
  176.            
  177.         } else {
  178.            
  179.             for (const std::string& phone_number : contact.phones) {
  180.                 contact_from_phone_book.add_phone_number(phone_number);
  181.             }
  182.  
  183.             contact_list.mutable_contact()->Add(std::move(contact_from_phone_book));
  184.         }      
  185.     }
  186.    
  187.     contact_list.SerializeToOstream(&output);
  188. }
  189.  
  190. PhoneBook DeserializePhoneBook(std::istream& input) {
  191.    
  192.     phone_book_serialize::ContactList contact_list;
  193.    
  194.     if (contact_list.ParseFromIstream(&input)) {
  195.        
  196.         std::vector<Contact> contact_list_vect(contact_list.contact_size());
  197.  
  198.         for (size_t i = 0; i < contact_list.contact_size(); ++i) {
  199.             contact_list_vect[i].name = contact_list.contact(i).name();
  200.            
  201.             if (contact_list.contact(i).has_birthday()) {
  202.                              
  203.                 Date birth_date = {contact_list.contact(i).birthday().year(),
  204.                                    contact_list.contact(i).birthday().month(),
  205.                                    contact_list.contact(i).birthday().day()};
  206.                
  207.                 contact_list_vect[i].birthday.emplace(birth_date);
  208.                
  209.             } else {
  210.                
  211.                 for (size_t j = 0; j < contact_list.contact(i).phone_number_size(); ++j) {
  212.                     contact_list_vect[i].phones.push_back(contact_list.contact(i).phone_number(j));
  213.                 }
  214.             }  
  215.         }
  216.        
  217.         return PhoneBook(contact_list_vect);
  218.     }
  219.    
  220.     return PhoneBook({});
  221. }
  222. -----------------------------------------------------------------------
  223. main.cpp
  224. -----------------------------------------------------------------------
  225. #include "phone_book.h"
  226.  
  227. #include <cassert>
  228. #include <sstream>
  229. #include <string_view>
  230.  
  231. #include <contact.pb.h>
  232.  
  233. using namespace std;
  234.  
  235. void TestSerialization() {
  236.    
  237.     const PhoneBook ab({
  238.         {"Ivan Ivanov"s, Date{1980, 1, 13}, {"+79850685521"s}},
  239.         {"Margarita Petrova"s, Date{1989, 4, 23}, {"+79998887766"s, "+71112223344"s}},
  240.         {"Just Birthday"s, Date{1989, 4, 23}, {}},
  241.         {"No Birthday"s, std::nullopt, {"+7-4862-77-25-64"s}},
  242.     });
  243.  
  244.     ostringstream output(std::ios::binary);
  245.     ab.SaveTo(output);
  246.  
  247.     istringstream input(output.str(), std::ios::binary);
  248.     phone_book_serialize::ContactList list;
  249.     assert(list.ParseFromIstream(&input));
  250.     assert(list.contact_size() == 4);
  251.  
  252.     unordered_map<string, const phone_book_serialize::Contact*> persons;
  253.    
  254.     for (const auto& p : list.contact()) {
  255.         persons[p.name()] = &p;
  256.     }
  257.  
  258.     {
  259.         auto it = persons.find("Ivan Ivanov"s);
  260.         assert(it != persons.end());
  261.         const auto& p = *it->second;
  262.  
  263.         assert(p.has_birthday());
  264.         assert(p.birthday().year() == 1980);
  265.     }
  266.     {
  267.         auto it = persons.find("Margarita Petrova"s);
  268.         assert(it != persons.end());
  269.         const auto& p = *it->second;
  270.  
  271.         assert(p.has_birthday());
  272.         assert(p.birthday().year() == 1989);
  273.         assert(p.phone_number_size() == 2);
  274.         assert(p.phone_number(1) == "+71112223344"sv);
  275.     }
  276.     {
  277.         auto it = persons.find("Just Birthday"s);
  278.         assert(it != persons.end());
  279.         const auto& p = *it->second;
  280.  
  281.         assert(p.has_birthday());
  282.         assert(p.birthday().day() == 23);
  283.         assert(p.phone_number_size() == 0);
  284.     }
  285.     {
  286.         auto it = persons.find("No Birthday"s);
  287.         assert(it != persons.end());
  288.         const auto& p = *it->second;
  289.  
  290.         assert(!p.has_birthday());
  291.         assert(p.phone_number_size() == 1);
  292.     }
  293. }
  294.  
  295. void TestDeserialization() {
  296.     phone_book_serialize::ContactList list;
  297.  
  298.     phone_book_serialize::Contact p;
  299.     p.set_name("Vanya"s);
  300.     p.add_phone_number("+7-953-181-46-12"s);
  301.  
  302.     *list.add_contact() = p;
  303.  
  304.     p.Clear();
  305.     p.set_name("Veronika"s);
  306.     p.mutable_birthday()->set_year(2000);
  307.     p.mutable_birthday()->set_month(3);
  308.     p.mutable_birthday()->set_day(31);
  309.     p.add_phone_number("325-87-16"s);
  310.  
  311.     *list.add_contact() = p;
  312.  
  313.     p.Clear();
  314.     p.set_name("Vitya"s);
  315.  
  316.     *list.add_contact() = p;
  317.  
  318.     ostringstream output(std::ios::binary);
  319.     list.SerializeToOstream(&output);
  320.  
  321.     istringstream input(output.str(), std::ios::binary);
  322.     const PhoneBook book = DeserializePhoneBook(input);
  323.  
  324.     auto range = book.FindByNamePrefix(""sv);
  325.     vector<Contact> contacts(range.begin(), range.end());
  326.  
  327.     assert(contacts.size() == 3);
  328.  
  329.     assert(contacts[0].name == "Vanya"sv);
  330.     assert(contacts[0].phones == (vector<string>{"+7-953-181-46-12"s}));
  331.     assert(!contacts[0].birthday);
  332.  
  333.     assert(contacts[1].name == "Veronika"sv);
  334.     assert(contacts[1].birthday);
  335.     assert(contacts[1].birthday->year == 2000);
  336.     assert(contacts[1].birthday->month == 3);
  337.     assert(contacts[1].birthday->day == 31);
  338.     assert(contacts[1].phones == (vector<string>{"325-87-16"s}));
  339.  
  340.     assert(contacts[2].name == "Vitya"sv);
  341.     assert(!contacts[2].birthday);
  342.     assert(contacts[2].phones.empty());
  343. }
  344.  
  345. void TestFindNameByPrefix() {
  346.     PhoneBook book({
  347.         {"Vasiliy Petrov"s, std::nullopt, {}},
  348.         {"Ivan Ivanov"s, std::nullopt, {}},
  349.         {"Vasiliy Ivanov"s, std::nullopt, {}},
  350.         {"Vasilisa Kuznetsova"s, std::nullopt, {}},
  351.         {"Ivan Petrov"s, std::nullopt, {}},
  352.         {"Vassisualiy Lokhankin"s, std::nullopt, {}},
  353.         {"Ivan Vasiliev"s, std::nullopt, {}},
  354.         {""s, std::nullopt, {}},
  355.     });
  356.  
  357.     auto get_names = [](PhoneBook::ContactRange range) {
  358.         vector<string> result;
  359.         for (const auto& record : range) {
  360.             result.push_back(record.name);
  361.         }
  362.         return result;
  363.     };
  364.  
  365.     assert(get_names(book.FindByNamePrefix("Ivan"sv)) == (vector<string>{"Ivan Ivanov"s, "Ivan Petrov"s, "Ivan Vasiliev"s}));
  366.     assert(get_names(book.FindByNamePrefix("I"sv)) == (vector<string>{"Ivan Ivanov"s, "Ivan Petrov"s, "Ivan Vasiliev"s}));
  367.     assert(get_names(book.FindByNamePrefix("Iv"sv)) == (vector<string>{"Ivan Ivanov"s, "Ivan Petrov"s, "Ivan Vasiliev"s}));
  368.     assert(get_names(book.FindByNamePrefix("Ivan Ivan"sv)) == (vector<string>{"Ivan Ivanov"s}));
  369.     assert(get_names(book.FindByNamePrefix("Vas"sv))
  370.            == (vector<string>{"Vasilisa Kuznetsova"s, "Vasiliy Ivanov"s, "Vasiliy Petrov"s, "Vassisualiy Lokhankin"s}));
  371.     assert(get_names(book.FindByNamePrefix("Vasili"sv))
  372.            == (vector<string>{"Vasilisa Kuznetsova"s, "Vasiliy Ivanov"s, "Vasiliy Petrov"s}));
  373.     assert(get_names(book.FindByNamePrefix("Vasiliy"sv)) == (vector<string>{"Vasiliy Ivanov"s, "Vasiliy Petrov"s}));
  374.     assert(get_names(book.FindByNamePrefix("Vasilis"sv)) == (vector<string>{"Vasilisa Kuznetsova"s}));
  375.     assert(get_names(book.FindByNamePrefix(""sv))
  376.            == (vector<string>{""s, "Ivan Ivanov"s, "Ivan Petrov"s, "Ivan Vasiliev"s, "Vasilisa Kuznetsova"s,
  377.                               "Vasiliy Ivanov"s, "Vasiliy Petrov"s, "Vassisualiy Lokhankin"s}));
  378. }
  379.  
  380. void TestFindNameByPrefix2() {
  381.     PhoneBook book({
  382.         {"a"s, std::nullopt, {}},
  383.         {"aaaa"s, std::nullopt, {}},
  384.         {"aabc"s, std::nullopt, {}},
  385.         {"aabccc"s, std::nullopt, {}},
  386.         {"aabcbc"s, std::nullopt, {}},
  387.         {"aeca"s, std::nullopt, {}},
  388.         {"aeca"s, std::nullopt, {}},
  389.         {"aefg"s, std::nullopt, {}},
  390.         {"aq"s, std::nullopt, {}},
  391.     });
  392.  
  393.     auto get_names = [](PhoneBook::ContactRange range) {
  394.         vector<string> result;
  395.         for (const auto& record : range) {
  396.             result.push_back(record.name);
  397.         }
  398.         return result;
  399.     };
  400.  
  401.     assert(book.FindByNamePrefix("a"sv).size() == 9u);
  402.     assert(get_names(book.FindByNamePrefix("aa"sv)) == (vector<string>{"aaaa"s, "aabc"s, "aabcbc"s, "aabccc"s}));
  403.  
  404.     assert(book.FindByNamePrefix("ab"sv).size() == 0u);
  405.     assert(book.FindByNamePrefix("b"sv).size() == 0u);
  406.     assert(book.FindByNamePrefix("aaaaa"sv).size() == 0u);
  407.     assert(book.FindByNamePrefix("aeb"sv).size() == 0u);
  408.     assert(book.FindByNamePrefix("aed"sv).size() == 0u);
  409.     assert(book.FindByNamePrefix("aeg"sv).size() == 0u);
  410.  
  411.     assert(get_names(book.FindByNamePrefix("aaa"sv)) == (vector<string>{"aaaa"s}));
  412.     assert(get_names(book.FindByNamePrefix("ae"sv)) == (vector<string>{"aeca"s, "aeca"s, "aefg"s}));
  413. }
  414.  
  415. int main() {
  416.     TestFindNameByPrefix();
  417.     TestFindNameByPrefix2();
  418.     TestSerialization();
  419.     TestDeserialization();
  420. }
Advertisement
Add Comment
Please, Sign In to add comment