22 Rozne QUEUE,std::bind,msgs, protobuf

Kontenery (vector, queue, list, map )
calbacki w c++ (std::bind expressions)
MSGS i testowanie
Google protobuf
C++17 If statement with initializer


                                                                                                                                              Kontenery 

                                                                                        Container adaptors(different interface: fifo, stack-LIFO,priority_queue )
std::queue If you need fast insertion and deletion at both its beginning and its end - FIFO data structure

Sequence containers (can be accessed sequentially)
std::vector dynamic array -If you need random access to elements
std::deque   double-ended queue
std::list doubly-linked list, when there are frequent insertions and deletions from the middle of the sequence  

Associative containers(can be quickly searched)
std::map  collection of key-value pairs, sorted by keys, keys are unique


Dodawanie elementów:

VECTOR:
można dodawać na początek i na konieć vektora (co go poszerza i zmienia iteratory)

mojv.push_back(10);      lub push_front


ale moża zastosować insert lub emplace , w miejsce iteratora wstawi wartość, ale nie zastąpi tylko przesunie resztę, jeśli it był początkiem, to wstawi na początek a dotychczasowy pierwszy będzie drugi (jeden i drugi)

mojv.insert(it,10);
mojv.emplace(it,10);

jeśli vektor był pusty nie zadziała, jeśli był stworzony tylko jako
vector<int> mojv ;

to musimy albo resize (i wypełni zerami lub przypadkowymi wartosciami

mojv.resize(10);              tym można też zmniejszyć

albo

myvec.assign(10,0);       (rozmiar, czym ma wypełnić ) też może zmniejszyć

CO WAŻNE OPERACJE MOGĄ UNIEWAŻNIĆ ITERATORY
vector<int>::iterator it;
it=mojv.begin();

JEŚLI DO ITERATORA DODAMY WARTOŚĆ WIĘKSZA NIŻ ROZMIAR ITERATORA I SPROBUJEMY ZAPISAC *(it)  TO NADPISZEMY PAMIĘĆ

DLATEGO LEPIEJ UZYWAĆ DOSTĘPU PRZEZ  AT

mojvector.at(liczba);

clear- czysci cały vector
erase(it) - czysci miejsce i zwraca iterator do nastepnego po nim

LISTA
Tworzenie
list<typ>  nazwa;

niemożliwy dostęp przez  []

iterator trzeba iterować, żeby dostać się do kolejnych elementów.

te same metody co Vektor

QUEUE
Element access:
front                   access the first element
back                   access the last element

Modifiers
push                   inserts element at the end
emplace(C++11)constructs element in-place at the end
pop                     removes the first element


Czym się różnią push i Emplace ?
Emplace takes the arguments necessary to construct an object in place, whereas push takes (a reference to) an object.
push                   copies objects into the queue.
emplace      construct them inside of the queue.


MAP inaczej słownik

zbiór par, klucz wartość, wstawianie wolne ale szybkie szukanie (drzewo się samo balansuje)
tylko 1 klucz na mapę i musi być porównywalny, dane jak w vektorze
złożoność obliczeniowa szukania lepsza niżliniowa (log N)

Tworzenie

map<typklucza,typdanych> nazwa={{1,"slowo"} , {2,"slowo2"},...

trzecim elementem w < > jest komparator do porównywania większe mniejsze, można go zmienić jak potrzeba, ale jak inny do nie pasuje do innych map bez takiego.






calbacki w c++
https://stackoverflow.com/questions/2298242/callback-functions-in-c
https://en.cppreference.com/w/cpp/utility/functional/placeholders



std::bind expressions

jeśli obiekt wewnętrzny ma wywołać metodę obiektu zewnętrznego to można by mu przekazać w konstruktorze wskaźnik na właściciela. Jesli jednak nie chceż używać zwykłych wskaźników, a zastosowanie smartpointera w takiej sytuacji jest kłopotliwe to można zostosować std:bind.

obiekt zewnętrzny tworzy typ calbacka oparty o std:function
using procCallback_t = std::function<void(ConnectionStateTp)>;

gdzie ConnectionStateTp  to typ argumentu funkcji

w obiekcie zewnętrznym tworzymy metodę "handleProcedures" wywoływaną calbackiem

void FRUConfServer::handleProcedures(ConnectionStateTp connectionState)
  {
    switch (connectionState)
    {
      case ConnectionStateTp::Connected:
        break;
      case ConnectionStateTp::Disconnected:
      case ConnectionStateTp::Error:
        removeConnection();
        break;
    }
  }


teraz do obiektu wewnetrznego przekazujemy namiary na powyzsza metode calbacka

m_connection->start(m_facade, std::bind(&FRUConfServer::handleProcedures, thisstd::placeholders::_1));


tylko jeden placeholder bo tylko jeden argument w calbacku
std::placeholders::_1

a teraz jak wygląda implementacja metody start obiektu wewnetrznego (ta która przyjmuje std::binda)

void FRUConfServerConnection::start(const facade_t::IMessagingFacade::Ptr& facadeprocCallback_t procedureCallback)
{
  ....tresc metody
  m_callback = procedureCallback;
}


i teraz mamy sposób jak wywołać z wewnętrznego obiektu metodę (calback) obiektu zewnętrznego

poniewaz był tylko jeden placeholder, mamy jeden argument (handleProcedures ma 1 arg)
wiec wywołanie to:
m_callback(ConnectionStateTp::Disconnected);

przykład w kodzie:

obiekt zewnetrzny:
/repo/eblzmrc/encl_ss/source/oamAdapterBl/FRUConfigurationServerSwU/src/FRUConfServer.cpp

obiekt wewnetrzny:
/repo/eblzmrc/encl_ss/source/oamAdapterBl/FRUConfigurationServerSwU/src/FRUConfServerConnection.cpp

Przykład Arka2 (2 placeholdery i jeden dodatkowy posrednik)
wew: /repo/eblzmrc/encl_ss/source/powerControlBl/dataChangeServerSwU/src/DataChangeServerConnection.cpp
posredni: /repo/eblzmrc/encl_ss/source/powerControlBl/dataChangeServerSwU/src/DataChangeServer.cpp
zew(z calbackiem):/repo/eblzmrc/encl_ss/source/powerControlBl/dataChangeServerSwU/src/DataChangeController.cpp



MSGS i testowanie
msgs link

przykład asynchronicznej komunikacji client i server łączą się z nameserwerem, nie ma procesu subskrypcji, klient wysyła do servere ind (tak zwane reverseind)
klient: /repo/eblzmrc/encl_ss/source/powerControlBl/FRUConfigurationClientSwU/src/FRUConfClient.cpp
server: /repo/eblzmrc/encl_ss/source/oamAdapterBl/FRUConfigurationServerSwU/src/FRUConfServer.cpp
serverCon: /repo/eblzmrc/encl_ss/source/oamAdapterBl/FRUConfigurationServerSwU/src/FRUConfServerConnection.cpp

server startowany w oamAdapterMAin.cpp
FRUServer->publish();

klient w AplicationPC
m_FRUClient_sp->connect();

klient ma metodę sendProductData która wysyła msg do servera.

Testowanie:
ponieważ kożystamy z MSGS i mamy nameserwer który łączy klienta i servera trzeba testować w specyficzny sposób:
jeden z testów klienta: /repo/eblzmrc/encl_ss/source/powerControlBl/FRUConfigurationClientSwU/test/src/FRUConfClientTest.cpp
TEST_F(FRUConfClientTest, sendIndication)
{
  triggerConnection();
  sut->sendProductData(ind);
  PROS__RECEIVE_MESSAGE(procedureTestFixture, SendProdDatRInd_t, procedureCall, indSent);
  EXPECT_THAT(trueisIndEqual(ind, indSent));
}


makro PROS__RECEIVE_MESSAGE symuluje msgs, tutaj oczekuje wiadomosci typu SendProdDatRInd_t  pod zmienna indSent1

ponieważ typ nie ma operatora == zastosowano matchery (patrz tresc testu)

możemy porównać to co wysyłaliśmy, z tym co "otrzymał" msgs zas pomoca EXPECT_THAT

w Przypadku testu servera : /repo/eblzmrc/encl_ss/source/oamAdapterBl/FRUConfigurationServerSwU/test/src/FRUConfServerTest.cpp
nic nie wysyłamy, wiec nie mozemy zastosować makra  PROS__RECEIVE_MESSAGE.
w Unacknowledge stosujemy zamiast tego makra:
uint32_t procedureRef = procedureTestFixture->newProcedureRef();

i wysyłka:
SendProdDatRInd_t indSent1;
  indSent1.set_productiondate(100);
  procedureTestFixture->dispatch(procedureRef, std::move(indSent1));

porównanie:
EXPECT_THAT(trueisIndEqual(ind, indSent1));
...

Testy dla synchronicznej komunikacji, patrz:
/repo/eblzmrc/encl_ss/source/oamAdapterBl/SSMConfigurationServerSwU/test/src/SSMConfServerTest.cpp
/repo/eblzmrc/encl_ss/source/powerControlBl/SSMConfigurationClientSwU/test/src/SSMConfClientTest.cpp

W tym przypadku server subskrybuje klienta (ktory otrzymuje cfm) i server rozsyła wiadomosci do zabsubskrybowanych.


Testy - symulacja onError i on Destroy
TEST_F(FRUConfServer, receivedOnError)
{
  triggerConnection();
  mockFacade->triggerError(cons_t::ConnectionError::REMOTE_TERMINATED);
  EXPECT_THAT(ConnectionState::Disconnected , sut->getConnectionState());
}


TEST_F(FRUConfServer, receivedOnDestroyed)
{
  triggerConnection();
  mockFacade->triggerDestroy();
  EXPECT_THAT(ConnectionState::Disconnected , sut->getConnectionState());
}


Tworzenie wiadomości i ustawianie jej pól:

auto rIndProcedure = pros_t::client::createUnacknowledgedRequest<PcChangeDataRInd_t>(m_facade->getProcedureManager(),
                                                                                         "DataChangeRInd");
    rbs::catiw1::ssm::dataChangeIf::PcChangeDataRInd rInd{};
    rInd.mutable_productdata()->set_productnumber(productData.getProductNumber());
    rInd.mutable_productdata()->set_productrevision(productData.getProductRevision());
    rInd.mutable_productdata()->set_productname(productData.getProductName());
    rInd.mutable_productdata()->set_productserialnumber(productData.getProductSerialNumber());
    rInd.mutable_productdata()->set_productiondate(productData.getProductionDate());
    rIndProcedure->send(std::move(rInd));


albo

rInd.set_allocated_productdata(
        new rbs::catiw1::ssm::commonDataTypes::ProductData(hwProductData.convertToGpbFormat()));
    rInd.set_allocated_powerprofiledata(
        new rbs::catiw1::ssm::commonDataTypes::PowerProfileData(hwPowerProfileData.convertToGpbFormat()));





Google protobuf
syntax="proto2";

package rbs.catiw1.ssm.commonDataTypes;

message ProductData
{
  optional string productnumber = 1;
  optional string productrevision = 2;
  optional string productname = 3;
  optional string productiondate = 4;
  optional string productserialnumber = 5;
}


tworzenie obiektu
rbs::catiw1::ssm::commonDataTypes::ProductData proData;

odczyt zawartości odbywa się przez potraktowanie pola jak metoda (wtedy staje sie get)
proData.productnumber();

zapis

proData.set_productnumber("101");

jeśli msg sklada się z innej wiadomości (np z tej powyzszej)

syntax="proto2";

import "CommonDataTypes.proto";

package rbs.catiw1.ssm.fruConfigurationHandlerIf;

enum InterfaceName
{
  FRU_CONFIGURATION_SERVER = 0;
}

message sendProductDataRInd{
    enum metaData{MESSAGE_NUMBER=0XBAD2900;};
    optional rbs.catiw1.ssm.commonDataTypes.ProductData productdata = 1;
}



to zapisujemy go wykorzystujac wczesniej stworzoną wiadomość wewnetrzną

rbs::catiw1::ssm::fruConfigurationHandlerIf::sendProductDataRInd ind;

ind.set_allocated_productdata(proData);

(w tym przypadku proData musi byc wskaźnikiem !!)

a odczytujemy go
ind.productdata().productnumber()


Jeśli chcemy zapisać Bez pośrednictwa obiektu proData:
ind.mutable_productdata()->set_productnumber("1");


C++17 If statement with initializer

// C++17 if statement with initializer
   if (int random_num = rand(); random_num % 2 == 0) {
      cout << random_num << " is an even number\n";
   } else {
      cout << random_num << " is an odd number\n";
   }



.