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);
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-chttps://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, this, std::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& facade, procCallback_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
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(true, isIndEqual(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(true, isIndEqual(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"; }