Let the data structure be anything (Array, Vectors, Std::Variant). But what is the difference in traversing using just for/for_each against std::visit.
for (auto& Element: collection){
std::visit([](auto arg){std::cout << arg << " ";}, Element);
}
And
for_each(collection.begin(), collection.end(), [](Element& e)
{
std::cout << e;
});
Note1: I know C-style for loop vs C++ for_each..For_each avoids typo error/syntactic sugar. so we can be happy with just for_each.
Note2: I know visitor design pattern too, which will be used for polymorphic object against polymorphic behaviour.
But still I couldn't appreciate and understand std::visit.
And when I digged further, I came to know something called Overload pattern.
template<typename ... Ts>
struct Overload : Ts ... {
using Ts::operator() ...;
};
template<class... Ts> Overload(Ts...) -> Overload<Ts...>;
But this Overload pattern too can be done using the same for_each right?
Thanks in advance for the reply.,
The difference between your first and second method is that the second does not work if your collection elements are variants. The example below compiles and lists your options.
Methods 2 and 4 are the only elegant ones I believe. But Method 2 is limited (see how it prints) while Method 4 gives you all the flexibility you may wish for.
But this does not necessarily mean Method 4 is the way to go. Place some intentional errors in Method 4 and then look at the error messages. They are mostly unintelligible.
#include <iostream>
#include <utility>
#include <variant>
#include <vector>
#include <algorithm>
using Element = std::variant<int, std::string>;
using Collection = std::vector<Element>;
Collection c = {1, 2, "three", "four", 5};
auto print_int = [](int const & e)
{
std::cout << "int: " << e << std::endl;
};
auto print_str = [](std::string const & e)
{
std::cout << "string: " << e << std::endl;
};
auto print_any = [](auto && e)
{
std::cout << "element: " << e << std::endl;
};
void Method1 ()
{
for (Element const & e: c)
{
{
auto const * i = std::get_if<int>(&e);
if (i)
{
print_int(*i);
continue;
}
}
{
auto const * s = std::get_if<std::string>(&e);
if (s)
{
print_str(*s);
continue;
}
}
}
}
void Method2 ()
{
for (Element const & e: c)
{
std::visit(print_any, e);
}
}
void Method3 ()
{
std::for_each(c.cbegin(), c.cend(), [](Element const & e)
{
std::visit(print_any, e);
});
}
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
void Method4 ()
{
for (Element const & e: c)
{
std::visit(overloaded
{
[](int i) { print_int(i); },
[](std::string s) { print_str(s) ; }
}, e);
}
}
int main ()
{
std::cout << std::endl << "Method1" << std::endl;
Method1();
std::cout << std::endl << "Method2" << std::endl;
Method2();
std::cout << std::endl << "Method3" << std::endl;
Method3();
std::cout << std::endl << "Method4" << std::endl;
Method4();
}
Related
I tried to implement an automation tool, and got problem when intercepting IME (Windows 10's default Microsoft IME) input Unicode strings (such as Japanese/Chinese).
I've written a 64-bit dll for injection to other processes/windows. The dll is as follows,
#include <windows.h>
#include <fstream>
#include <locale>
// for output only
static wchar_t* className(HWND hwnd) {
static wchar_t className[128];
::GetClassNameW(hwnd, className, 128);
return className;
}
extern "C" __declspec(dllexport) LRESULT CALLBACK ImeCallback(int code, WPARAM wParam, LPARAM lParam) {
std::wofstream out("test.log", std::ios::app);
out.imbue(std::locale("zh_CN.UTF-8"));
if (code >= 0)
{
PCWPSTRUCT msg = (PCWPSTRUCT)lParam;
if (msg->message == WM_IME_COMPOSITION) {
out << "composition: " << className(msg->hwnd) << ":";
if (msg->lParam & GCS_COMPATTR) {
out << ":GCS_COMPATTR";
}
if (msg->lParam & GCS_COMPCLAUSE) {
out << ":GCS_COMPCLAUSE";
}
if (msg->lParam & GCS_COMPREADSTR) {
out << ":GCS_COMPREADSTR";
}
if (msg->lParam & GCS_COMPREADATTR) {
out << ":GCS_COMPREADATTR";
}
if (msg->lParam & GCS_COMPREADCLAUSE) {
out << ":GCS_COMPREADCLAUSE";
}
if (msg->lParam & GCS_COMPSTR) {
out << ":GCS_COMPSTR";
}
if (msg->lParam & GCS_CURSORPOS) {
out << ":GCS_CURSORPOS";
}
if (msg->lParam & GCS_DELTASTART) {
out << ":GCS_DELTASTART";
}
if (msg->lParam & GCS_RESULTCLAUSE) {
out << ":GCS_RESULTCLAUSE";
}
if (msg->lParam & GCS_RESULTREADCLAUSE) {
out << ":GCS_RESULTREADCLAUSE";
}
if (msg->lParam & GCS_RESULTREADSTR) {
out << ":GCS_RESULTREADSTR";
}
if (msg->lParam & GCS_RESULTSTR) {
out << ":GCS_RESULTSTR";
}
out << std::endl;
if (msg->lParam & GCS_RESULTSTR) {
wchar_t data[128] = { 0 };
HIMC context = ImmGetContext(msg->hwnd);
ImmGetCompositionStringW(context, GCS_RESULTSTR, data, 255);
ImmReleaseContext(msg->hwnd, context);
out << " result data: " << data << std::endl;
}
else if (msg->lParam & GCS_COMPSTR) {
wchar_t data[128] = { 0 };
HIMC context = ImmGetContext(msg->hwnd);
ImmGetCompositionStringW(context, GCS_COMPSTR, data, 255);
ImmReleaseContext(msg->hwnd, context);
out << " composition data: " << data << std::endl;
}
}
else if (msg->message == WM_IME_STARTCOMPOSITION) {
out << "start composition" << std::endl;
}
else if (msg->message == WM_IME_ENDCOMPOSITION)
{
out << "end composition" << std::endl;
}
else if (msg->message == WM_IME_SETCONTEXT) {
out << "set context : " << className(msg->hwnd) << ":" << (wParam ? "TRUE" : "FALSE") << std::endl;
switch (msg->lParam) {
case ISC_SHOWUICOMPOSITIONWINDOW:
out << " ISC_SHOWUICOMPOSITIONWINDOW" << std::endl;
break;
case ISC_SHOWUIGUIDELINE:
out << " ISC_SHOWUIGUIDELINE" << std::endl;
break;
case ISC_SHOWUIALLCANDIDATEWINDOW:
out << " ISC_SHOWUIALLCANDIDATEWINDOW" << std::endl;
break;
case ISC_SHOWUIALL:
out << " ISC_SHOWUIALL" << std::endl;
break;
case ISC_SHOWUICANDIDATEWINDOW:
out << " ISC_SHOWUICANDIDATEWINDOW" << std::endl;
break;
case ISC_SHOWUICANDIDATEWINDOW << 1:
out << " ISC_SHOWUICANDIDATEWINDOW << 1" << std::endl;
break;
case ISC_SHOWUICANDIDATEWINDOW << 2:
out << " ISC_SHOWUICANDIDATEWINDOW << 2" << std::endl;
break;
case ISC_SHOWUICANDIDATEWINDOW << 3:
out << " ISC_SHOWUICANDIDATEWINDOW << 3" << std::endl;
break;
default:
out << " default" << std::endl;
}
}
else if (msg->message == WM_IME_NOTIFY) {
HIMC context;
wchar_t data[128] = { 0 };
out << "notify : " << className(msg->hwnd) << std::endl;
switch (msg->wParam) {
case IMN_CHANGECANDIDATE:
out << " IMN_CHANGECANDIDATE" << std::endl;
break;
case IMN_CLOSECANDIDATE:
out << " IMN_CLOSECANDIDATE" << std::endl;
break;
case IMN_CLOSESTATUSWINDOW:
out << " IMN_CLOSESTATUSWINDOW" << std::endl;
break;
case IMN_GUIDELINE:
out << " IMN_GUIDELINE" << std::endl;
break;
case IMN_OPENCANDIDATE:
out << " IMN_OPENCANDIDATE" << std::endl;
break;
case IMN_OPENSTATUSWINDOW:
out << " IMN_OPENSTATUSWINDOW" << std::endl;
break;
case IMN_SETCANDIDATEPOS:
out << " IMN_SETCANDIDATEPOS" << std::endl;
break;
case IMN_SETCOMPOSITIONFONT:
out << " IMN_SETCOMPOSITIONFONT" << std::endl;
break;
case IMN_SETCOMPOSITIONWINDOW:
out << " IMN_SETCOMPOSITIONWINDOW" << std::endl;
break;
case IMN_SETCONVERSIONMODE:
out << " IMN_SETCONVERSIONMODE" << std::endl;
break;
case IMN_SETOPENSTATUS:
out << " IMN_SETOPENSTATUS" << std::endl;
break;
case IMN_SETSENTENCEMODE:
out << " IMN_SETSENTENCEMODE" << std::endl;
break;
case IMN_SETSTATUSWINDOWPOS:
out << " IMN_SETSTATUSWINDOWPOS" << std::endl;
break;
default:
out << " default: " << msg->wParam << ":" << msg->lParam << std::endl;
context = ImmGetContext(msg->hwnd);
auto result = ImmGetCompositionStringW(context, GCS_RESULTSTR, data, 256);
ImmReleaseContext(msg->hwnd, context);
out << " data: " << data << std::endl;
}
}
else if (msg->message == WM_IME_CHAR) {
out << "char:" << msg->wParam << std::endl;
}
else if (msg->message == WM_IME_CONTROL) {
out << "control" << std::endl;
}
else if (msg->message == WM_IME_SELECT) {
out << "select : " << className(msg->hwnd) << " : " << (msg->wParam ? "TRUE" : "FALSE") << " : " << msg->lParam << std::endl;
}
else if (msg->message == WM_IME_KEYDOWN) {
out << "key down" << std::endl;
}
else if (msg->message == WM_IME_KEYUP) {
out << "key up" << std::endl;
}
else if (msg->message == WM_IME_REQUEST) {
out << "request" << std::endl;
}
}
out.close();
return ::CallNextHookEx(0, code, wParam, lParam);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
I then wrote a 64-bit .NET application to inject this dll.
public delegate long HookProc(long code, long wParam, long lParam);
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", SetLastError = true)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
public static extern IntPtr FreeLibrary([In] IntPtr hModule);
[DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true, ThrowOnUnmappableChar = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int hookType, HookProc hookFn, IntPtr hMod, int threadId);
[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(int hookId);
public int hookId;
private void button1_Click(object sender, EventArgs e)
{
IntPtr hMod = LoadLibrary("ImeHook.dll");
IntPtr callback = GetProcAddress(hMod, "ImeCallback");
// 4 represents WH_CALLWNDPROC
// Try to inject into all other 64-bits applications
hookId = SetWindowsHookEx(4, (HookProc)Marshal.GetDelegateForFunctionPointer(callback, typeof(HookProc)), hMod, 0);
}
private void button2_Click(object sender, EventArgs e)
{
UnhookWindowsHookEx(hookId);
}
The program can indeed inject the DLL into other processes because I can see messages from various windows in the logging file.
I can even intercept the IME Unicode strings in the WM_IME_COMPOSITION message (GCS_RESULTSTR), but only for some applications such as 64-bit forms written by .NET.
For some other applications, such as Firefox and Microsoft Edge, I can only see some WM_IME_NOTIFY messages, and there are no any WM_IME_COMPOSITION/WM_IME_STARTCOMPOSITION/WM_IME_ENDCOMPOSITION messages received for these windows. Therefore I cannot get the final Unicode input strings for these windows.
Did I do something wrong? Is it even possible to get such information for all processes or windows (such as 64-bit Edge/Chrome/Firefox) in Windows 10?
I also tried the method you described in this question but failed to capture the windows IME output Unicode string(Chinese)
I found something from the Microsoft docs site, after windows XP, windows introduce the Text Services Framework, and the TSF manager functions as a mediator between an application and one or more text services, maybe there is no message like WM_IME_COMPOSITION or WM_CHAR sent by the IME for Firefox/Chrome/World in latest OS(windows 10 or 11)
but when I use Notepad, still can get the Unicode string of Chinese by hook WH_CALLWNDPROC
For a homework assignment i need to create a program that lets you play the Irish card game 25. I can kind of have my code give out a hand to one person but if i try to have multiple people, the code gives out an identical hand to the other people. How do i give different, unique hands to other people?
I've tried using a loop, thinking that the function would simply reset the array but it hasn't
/* Deals a random hand of cards */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
#define BOOL int
#define NUM_SUITS 4
#define NUM_RANKS 13
int DealCards(int i);
int main()
{
int i;
int NumOfPlayers;
printf("Please Enter the Number Of Players: ");
scanf("%d", &NumOfPlayers);
for (i = 1; i <= NumOfPlayers; i++)
{
DealCards(i);
}
}
int DealCards(int i)
{
BOOL in_hand[NUM_SUITS][NUM_RANKS] = { FALSE };
int num_cards = 5, rank, suit;
const char rank_code[] = { '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', 'A' };
const char suit_code[] = { 'C', 'D', 'H', 'S' };
srand(time(NULL));
printf("\n\nPlayer %d's hand :\n", i);
while (num_cards > 0)
{
suit = rand() % NUM_SUITS;
rank = rand() % NUM_RANKS;
if (!in_hand[suit][rank])
{
in_hand[suit][rank] = TRUE;
num_cards--;
printf(" %cof%c ", rank_code[rank], suit_code[suit]);
}
printf("\n");
}
return 0;
}
The problem is that you call srand() function before giving card to every player. You use time(NULL) as an argument, therefore seed changes only every second, and players get cards with the same seed.
You need to set seed only once for each program launch.
Your current method is drawing cards with replacement, then checking if that has been drawn. It's rather easy, and a better model of the game, to shuffle the deck.
What you should do is define a type that encodes a particular card, populate a collection of that type, with each card value in use, shuffle the deck, then assign cards from the shuffled deck.
As a sketch
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <iostream>
const std::vector<std::string> rank_code = { "2","3","4","5","6","7","8","9","10","Jack","Queen","King","Ace" };
const std::vector<std::string> suit_code = { "Clubs","Diamonds","Hearts","Spades" };
const int num_cards = 5;
struct Card
{
Card(char s, char r) : suit(s), rank(r) {}
char suit;
char rank;
};
std::ostream & operator<< (std::ostream & os, const Card & c)
{
return os << rank_code[c.rank] << " of " << suit_code[c.suit];
}
using Deck = std::vector<Card>;
Deck freshDeck()
{
Deck result;
for (std::size_t s = 0; s < suit_code.size(); ++s)
for (std::size_t r = 0; r < rank_code.size(); ++r)
result.emplace_back(s, r);
return result;
}
void dealCards(int player, Deck & deck)
{
std::string joiner;
std::cout << std::endl << "Player " << player << "'s hand" << std::endl;
for (int c = 0; c < num_cards; ++c)
{
std::cout << joiner << deck.back();
deck.pop_back();
joiner = ", ";
}
std::cout << std::endl;
}
int main ()
{
std::mt19937 gen{ std::random_device{}() };
Deck deck = freshDeck();
std::shuffle(deck.begin(), deck.end(), gen);
std::cout << "Enter number of players" << std::endl;
int num_players;
std::cin >> num_players;
for (int p = 1; p <= num_players; ++p)
{
dealCards(p, deck);
}
}
Just initialize your srand(time(NULL)) before giving cards
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
#define BOOL int
#define NUM_SUITS 4
#define NUM_RANKS 13
int DealCards(int i);
int main()
{
int i;
int NumOfPlayers;
printf("Please Enter the Number Of Players: ");
scanf("%d",&NumOfPlayers);
//here for example
srand(time(NULL));
for (i = 1; i <= NumOfPlayers; i++)
{
DealCards(i);
}
}
int DealCards(int i) {
BOOL in_hand[NUM_SUITS][NUM_RANKS] = { FALSE };
int num_cards = 5, rank, suit;
const char rank_code[] = { '2','3','4','5','6','7','8',
'9','10','11','12','13','A' };
const char suit_code[] = { 'C','D','H','S' };
printf("\n\nPlayer %d's hand :\n",i);
while (num_cards > 0) {
suit = rand() % NUM_SUITS;
rank = rand() % NUM_RANKS;
if (!in_hand[suit][rank])
{
in_hand[suit][rank] = TRUE;
num_cards--;
printf(" %cof%c\n", rank_code[rank], suit_code[suit]);
}
}
return 0;
}
The problem is that the srand is initialized with the same value each time it is run.
So the same random values are generated in the same order. This is why all the hands are the same.
srand and rand are from the <cstdlib> header.
In C++11 and higher it is better to use functions from <random> header.
Also C++ gives many possibilities to use object orientated programming and has an extensive library of data structures and algorithms. I would suggest to use std::vector or std::array instead of plain arrays. And also use std::shuffle to get a random order of cards.
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <iostream>
#include <exception>
//////////////////////////////////////////////////////////////////////////////////////////
class Rank
{
std::string value;
Rank(std::string value) : value(value) {}
public:
static const std::vector< Rank >& get_all()
{
static std::vector< Rank > suits = { { "2" }, { "3" }, { "4" }, { "5" }, { "6" },
{ "7" }, { "8" }, { "9" }, { "10" }, { "J" },
{ "Q" }, { "K" }, { "A" } };
return suits;
}
const std::string& to_string() const { return value; }
};
//////////////////////////////////////////////////////////////////////////////////////////
class Suit
{
std::string value;
Suit(std::string value) : value(value) {}
public:
static const std::vector< Suit >& get_all()
{
static std::vector< Suit > ranks = {
{ "Clubs" }, { "Diamonds" }, { "Hearts" }, { "Spades" }
};
return ranks;
}
const std::string& to_string() const { return value; }
};
//////////////////////////////////////////////////////////////////////////////////////////
class Card
{
Suit suit;
Rank rank;
public:
Card(const Suit& suit, const Rank& rank) : suit(suit), rank(rank) {}
std::string to_string() const { return rank.to_string() + " of " + suit.to_string(); }
};
//////////////////////////////////////////////////////////////////////////////////////////
class Deck
{
std::vector< Card > cards;
public:
Deck()
{
const auto& ranks = Rank::get_all();
const auto& suits = Suit::get_all();
cards.reserve(ranks.size() * suits.size());
for (const Suit& s : suits)
for (const Rank& r : ranks)
cards.emplace_back(s, r);
}
void shuffle()
{
static std::random_device rd;
static std::mt19937 g(rd());
std::shuffle(cards.begin(), cards.end(), g);
}
std::size_t cards_count() const { return cards.size(); }
Card get_top_card()
{
if (cards_count() == 0)
throw std::logic_error("No more cards!");
const auto card = cards.back();
cards.pop_back();
return card;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
int get_player_count()
{
std::cout << "Please enter the number of players:" << std::endl;
int player_count;
std::cin >> player_count;
return player_count;
}
//////////////////////////////////////////////////////////////////////////////////////////
void deal_cards(int player_count, int cards_to_deal, Deck& deck)
{
for (auto player_num = 1; player_num <= player_count; player_num++)
{
std::cout << "\n\nPlayer " << player_num << "'s hand :\n";
for (auto card_count = 0; card_count < cards_to_deal; card_count++)
{
if (deck.cards_count() == 0)
{
std::cout << "\n\nNo more cards to deal!" << std::endl;
return;
}
std::cout << deck.get_top_card().to_string() << ", ";
}
}
std::cout << std::endl;
}
//////////////////////////////////////////////////////////////////////////////////////////
int main()
{
Deck deck;
deck.shuffle();
const auto player_count = get_player_count();
const auto cards_to_deal = 5;
deal_cards(player_count, cards_to_deal, deck);
}
//////////////////////////////////////////////////////////////////////////////////////////
I am learning more about smart pointers in C++14.
Consider the following MWC:
#include <iostream>
#include <string>
#include <memory>
class House {
public:
House &operator=(const House &house) = default;
House(const House &house) = default;
House(): id_habitants_(nullptr), num_habitants_() {}
explicit House(size_t num_habitants) {
if (num_habitants > 0) {
num_habitants_ = num_habitants;
id_habitants_ = new int[num_habitants_];
if (id_habitants_ != nullptr) {
for (size_t id = 0; id < num_habitants_; ++id) {
id_habitants_[id] = 1;
}
}
}
}
void Print() {
if (id_habitants_ != nullptr) {
for (size_t id = 0; id < num_habitants_; ++id) {
std::cout << id_habitants_[id] << ' ';
}
std::cout << std::endl;
} else {
std::cout << "<empty>" << std::endl;
}
}
~House() {
if (id_habitants_ != nullptr) {
delete [] id_habitants_;
}
num_habitants_ = 0;
}
private:
int *id_habitants_;
size_t num_habitants_;
};
int main() {
std::cout << "Testing unique_ptr.\n" << std::endl;
std::cout << "Using a dumb House class..." << std::endl;
std::cout << "Creating House h1 with 3 habitants..." << std::endl;
House h1(3);
std::cout << "IDs of h1's 3 habitants:" << std::endl;
h1.Print();
std::cout << "Creating House h2 with 0 habitants..." << std::endl;
House h2;
std::cout << "IDs of h2's 0 habitants:" << std::endl;
h2.Print();
std::cout << "Default-assigning h1 to h2..." << std::endl;
h2 = h1;
std::cout << "IDs of h2's new 3 habitants:" << std::endl;
h2.Print();
std::cout << "Destroying h1..." << std::endl;
h1.~House();
std::cout << "IDs of h2's new 3 habitants:" << std::endl;
h2.Print();
}
Without modifying the default copy constructor and the default assignment operator for the class House, how can I ensure correct pointer behavior during assignment via smart pointers?
On a first try it seems like using std::unique_ptr would be the way to go. I could create a new class:
class SmartHouse {
public:
SmartHouse &operator=(const SmartHouse &shouse) = default;
SmartHouse(const SmartHouse &shouse) = default;
SmartHouse(): id_habitants_(nullptr), num_habitants_() {}
explicit SmartHouse(size_t num_habitants) {
if (num_habitants > 0) {
num_habitants_ = num_habitants;
id_habitants_ = std::unique_ptr<int[]>(new int[num_habitants_]);
if (id_habitants_) {
for (size_t id = 0; id < num_habitants_; ++id) {
id_habitants_[id] = 1;
}
}
}
}
void Print() {
if (id_habitants_) {
for (size_t id = 0; id < num_habitants_; ++id) {
std::cout << id_habitants_[id] << ' ';
}
std::cout << std::endl;
} else {
std::cout << "<empty>" << std::endl;
}
}
~SmartHouse() {
num_habitants_ = 0;
}
private:
std::unique_ptr<int[]> id_habitants_;
size_t num_habitants_;
};
According to this, I can't really copy one unique pointer to another. Makes sense, right? It sort of defeats the purpose of it being unique. I.e. this would not compile:
SmartHouse sh1(3);
SmartHouse sh2;
sh2 = sh1;
But I could specify a move assignment operator and have the unique_ptr<int[]> member be moved upon assignment thus transferring ownership of the pointed data to the left object upon assignment:
class SmartHouse {
SmartHouse &operator=(SmartHouse &&SmartHouse) = default;
}
...
SmartHouse sh1(3);
SmartHouse sh2;
sh2 = std::move(sh1);
sh1.~SmartHouse();
sh2.Print();
Core question: Does this make sense at all? Are there better ways to enhance assignment of pointer member variables?
Full MWE.
I have a two dimensional float array in QML. How do I get its values in C++.
I have created a class in c++ and have done the part of qmlRegisterType. The class is now accessible in QML.
Please demonstrate with a small example.
Here's what I have tried:
Header:
#include <QQuickItem>
#include <iostream>
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList <QVariantList> names READ names WRITE setnames NOTIFY namesChanged)
QList <QVariantList> m_names;
public:
Controller()
{
}
~Controller() {
}
QList <QVariantList> names() const
{
return m_names;
}
public slots:
void setnames(QList <QVariantList> arg)
{
QVariantList p;
if (arg.size () > 0)
{
p = arg.first ();
std::cout << "\narg: \n" << p[0].toInt ();
}
else
std::cout << "\nqqqq " << arg.size () << "\n";
}
signals:
void namesChanged(QList <QVariantList> arg);
};
qml
import QtQuick 2.0
import FromCpp 1.0
Rectangle
{
property variant arras: [[1,2,3], [4,5,6]]
Controller
{
id: ppp
}
MouseArea
{
anchors.fill: parent
onClicked:
{
ppp.setnames(arras)
console.log(arras.length)
}
}
}
The exact output from QtCreator:
Starting /home/***/documents/test/build-junk-Desktop_Qt_5_1_0_GCC_64bit-Debug/junk...
QML debugging is enabled. Only use this in a safe environment.
2
qqqq 0
QThreadStorage: Thread 0x181e270 exited after QThreadStorage 2 destroyed
/home/***/documents/test/build-junk-Desktop_Qt_5_1_0_GCC_64bit-Debug/junk exited with code 0
///
Here, as you can see the size of the 2D array from QML is printed correctly as 2 and the size is printed 0 from c++.
Why is that happening? Please explain.
According to user1095108's answer, in order to access the internal elements of the QML's 2 Dim array passed to C++, we need to convert each row to a list as follows:
QML part:
import QtQuick 2.0
import FromCpp 1.0
Rectangle
{
property variant twoDimArray: [[1,2,3], [4,5,6]]
Controller
{
id: controllerA
}
MouseArea
{
anchors.fill: parent
onClicked:
{
controllerA.setname (twoDimArray)
}
}
}
C++ part:
void setname (QVariantList arg)
{
if (arg.size())
{
QList <QVariant> p = arg[0].toList();
std::cout << "\nRow0 0:" << p[0].toInt ();
std::cout << "\nRow0 1:" << p[1].toInt ();
std::cout << "\nRow0 2:" << p[2].toInt ();
std::cout << "\n";
QList <QVariant> p1 = arg[1].toList();
std::cout << "\nRow1 0:" << p1[0].toInt ();
std::cout << "\nRow1 1:" << p1[1].toInt ();
std::cout << "\nRow1 2:" << p1[2].toInt ();
}
}
Output:
Starting /home/.../documents/test/build-junk-Desktop_Qt_5_1_0_GCC_64bit-Debug/junk...
QML debugging is enabled. Only use this in a safe environment.
Row0 0:1
Row0 1:2
Row0 2:3
Row1 0:4
Row1 1:5
Row1 2:6/home/.../documents/test/build-junk-Desktop_Qt_5_1_0_GCC_64bit-Debug/junk exited with code 0
It is actually very simple. Javascript arrays map to QVariantLists, Javascript objects to QVariantMaps. Therefore, you are dealing with nested QVariantLists. Try this:
void setnames(QVariantList const& arg)
{
if (arg.size())
{
auto const p(arg.front().toList());
std::cout << "\narg: \n" << p.front().toInt();
}
else
std::cout << "\nqqqq " << arg.size () << "\n";
}
This is my method :
#include <QQuickItem>
#include <QDebug>
#include <QVariantList>
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant names READ names WRITE setnames NOTIFY namesChanged)
QVariant m_names;
public:
Controller()
{
}
~Controller() {
}
QVariant names() const
{
return m_names;
}
public slots:
void setnames(QVariant arg)
{
QVariantList dim1 = arg.toList();
qDebug() << "outer dimension size" << dim1.size();
for(int i=0;i<dim1.size();++i)
{
QVariantList &dim2 = dim1.at(i).toList();
qDebug() << "inner dimension size at" << dim2.size();
}
}
signals:
void namesChanged();
};
Try this:
Somewhere in QML file:
property variant floats: [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]
Includes:
#include <QtQuick/QQuickView>
#include <QQmlContext>
#include <QQmlProperty>
#include <QQuickItem>
In C++, in constructor for example:
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view, ui->widget);//I show view in my widget
container->setMinimumSize(ui->widget->size());
container->setMaximumSize(ui->widget->size());
view->setSource(QUrl::fromLocalFile("pathToQMLFile"));
//starts here
QQmlProperty property(view->rootObject(), "floats");//get rootObject and set name of property
QVariantList lst = property.read().toList();//use raed method to get QVariant and convert it to list
for (int i = 0; i < lst.length(); ++i)
{
qDebug() << lst.at(i).toFloat();//show list
}
Output:
1.1
2.2
3.3
4.4
5.5
6.6
I hope my code shows main idea and you can use it in your class, because the most important job(how to get numbers) was done.
It looks like you can use QVariantList (or QList<QVariant>):
#include <QtQuick>
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList names READ names WRITE setnames NOTIFY namesChanged)
QVariantList m_names;
public:
Controller()
{
}
~Controller() {
}
QVariantList names() const
{
return m_names;
}
public slots:
void setnames(QVariantList arg)
{
qDebug() << arg;
}
signals:
void namesChanged(QVariantList arg);
};
#include <QApplication>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
qmlRegisterType<Controller>("FromCpp", 1, 0, "Controller");
QQuickView view(QUrl(QStringLiteral("main.qml")));
view.show();
return app.exec();
}
#include "main.moc"
Output:
(QVariant(QVariantList, (QVariant(int, 1) , QVariant(int, 2) , QVariant(int, 3) ) ) , QVariant(QVariantList, (QVariant(int, 4) , QVariant(int, 5) , QVariant(int, 6) ) ) )
Im constantly getting this when I build on Qtcreator.
*** glibc detected *** /home/Exxamples/EffectivCons: realloc(): invalid pointer: 0xb6fb5414 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75ee2)[0xb6603ee2]
/lib/i386-linux-gnu/libc.so.6(realloc+0x25d)[0xb660856d]
/lib/i386-linux-gnu/libc.so.6(realloc+0x273)[0xb6608583]
/opt/Qt5.1.1/5.1.1/gcc/lib/libQt5Core.so.5(_ZN9QListData7reallocEi+0x37)[0xb6906e67]
/opt/Qt5.1.1/5.1.1/gcc/lib/libQt5Core.so.5(_ZN9QListData6appendEi+0x7c)[0xb6906f4c]
/opt/Qt5.1.1/5.1.1/gcc/lib/libQt5Core.so.5(_ZN9QListData6appendEv+0x23)[0xb6906fd3]
/opt/Qt5.1.1/5.1.1/gcc/lib/libQt5Core.so.5(+0x1afa3e)[0xb6a05a3e]
/opt/Qt5.1.1/5.1.1/gcc/lib/libQt5Core.so.5(_Z21qRegisterResourceDataiPKhS0_S0_+0x216)[
This is my code, through the other questions on the website, I saw some users using malloc or realloc, but Im not using it, why am i getting an error?
#include <termios.h>
#include <string.h>
#include <stdlib.h>
#define BUFLEN 512
std::string numberToString(const int n);
void sendExpressivAnimation(int,EmoStateHandle eState);
void handleExpressivEvent(std::ostream& os, EmoEngineEventHandle expressivEvent);
bool handleUserInput();
void promptUser();
void nonblocking();
int getch();
int kbhit();
int createSocket();
int startSendPort = 30000;
struct termios initial_settings, new_settings;
int kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &rdfs);
}
int main(int argc, char **argv)
{
EmoEngineEventHandle eEvent = EE_EmoEngineEventCreate();
EmoStateHandle eState = EE_EmoStateCreate();
unsigned int userID = 0;
const int CONTROL_PANEL_PORT = 3008;
bool connected = false;
if(EE_EngineRemoteConnect("127.0.0.1", CONTROL_PANEL_PORT)== EDK_OK)
{
std::cout <<"Emotiv Engine started" << std::endl;
connected = true;
}
else
{
std::cout <<"Emotiv Engine failed !"<< std::endl;
connected = false;
}
std::cout << "Type \"exit\" to quit, \"help\" to list available commands..." << std::endl;
promptUser();
int _socket;
_socket = createSocket();
if(connected)
{
nonblocking();
fflush(stdin);
while (true) {
// Handle the user input
//if (_kbhit()) {
if (!handleUserInput()) {
break;
}
//}
int state = EE_EngineGetNextEvent(eEvent);
// New event needs to be handled
if (state == EDK_OK) {
EE_Event_t eventType = EE_EmoEngineEventGetType(eEvent);
EE_EmoEngineEventGetUserId(eEvent, &userID);
switch (eventType) {
// New headset connected, create a new socket to send the animation
case EE_UserAdded:
{
std::cout << std::endl << "New user " << userID << " added, sending Expressiv animation to ";
std::cout << "127.0.0.1" << ":" << startSendPort << "..." << std::endl;
promptUser();
break;
}
// Headset disconnected, remove the existing socket
case EE_UserRemoved:
{
std::cout << std::endl << "User " << userID << " has been removed." << std::endl;
promptUser();
break;
}
// Send the Expressiv animation if EmoState has been updated
case EE_EmoStateUpdated:
{
//std::cout << "New EmoState from user " << userID << "..." << std::endl;
EE_EmoEngineEventGetEmoState(eEvent, eState);
sendExpressivAnimation(_socket,eState);
break;
}
// Handle Expressiv training event
case EE_ExpressivEvent:
{
handleExpressivEvent(std::cout, eEvent);
break;
}
default:
break;
}
}
else if (state != EDK_NO_EVENT) {
std::cout << std::endl << "Internal error in Emotiv Engine!" << std::endl;
break;
}
}
}
EE_EngineDisconnect();
EE_EmoStateFree(eState);
EE_EmoEngineEventFree(eEvent);
return 0;
}
std::string numberToString(const int n) {
char* buf;
//_itoa(n, buf, 10);
QString nStr = QString::number(n);
buf = nStr.toLocal8Bit().data();
return std::string(buf);
}
void sendExpressivAnimation(int _socket,EmoStateHandle eState) {
std::ostringstream output;
EE_ExpressivAlgo_t upperFaceType = ES_ExpressivGetUpperFaceAction(eState);
EE_ExpressivAlgo_t lowerFaceType = ES_ExpressivGetLowerFaceAction(eState);
float upperFaceAmp = ES_ExpressivGetUpperFaceActionPower(eState);
float lowerFaceAmp = ES_ExpressivGetLowerFaceActionPower(eState);
if (ES_ExpressivIsBlink(eState)) {
output << "B,";
}
if (ES_ExpressivIsLeftWink(eState)) {
output << "l,";
}
if (ES_ExpressivIsRightWink(eState)) {
output << "r,";
}
if (ES_ExpressivIsLookingRight(eState)) {
output << "R,";
}
if (ES_ExpressivIsLookingLeft(eState)) {
output << "L,";
}
if (upperFaceAmp > 0.0) {
switch (upperFaceType) {
case EXP_EYEBROW: output << "b"; break;
case EXP_FURROW: output << "f"; break;
default: break;
}
output << numberToString(static_cast<int>(upperFaceAmp*100.0f)) << ",";
}
if (lowerFaceAmp > 0.0) {
switch (lowerFaceType) {
case EXP_CLENCH: output << "G"; break;
case EXP_SMILE: output << "S"; break;
case EXP_LAUGH: output << "H"; break;
case EXP_SMIRK_LEFT: output << "sl"; break;
case EXP_SMIRK_RIGHT: output << "sr"; break;
default: break;
}
output << numberToString(static_cast<int>(lowerFaceAmp*100.0f)) << ",";
}
std::string outString = output.str();
// Remove the last comma
if (outString.length()) {
outString.resize(outString.length()-1);
}
if (!outString.length()) {
outString = std::string("neutral");
}
if(send(_socket, outString.c_str(), BUFLEN, 0)==-1)
{
std::cout<<"sending error"<<std::endl;
//exit(1);
}
}
void handleExpressivEvent(std::ostream& os, EmoEngineEventHandle expressivEvent) {
unsigned int userID = 0;
EE_EmoEngineEventGetUserId(expressivEvent, &userID);
EE_ExpressivEvent_t eventType = EE_ExpressivEventGetType(expressivEvent);
switch (eventType) {
case EE_ExpressivTrainingStarted:
{
os << std::endl << "Expressiv training for user " << userID << " STARTED!" << std::endl;
break;
}
case EE_ExpressivTrainingSucceeded:
{
os << std::endl << "Expressiv training for user " << userID << " SUCCEEDED!" << std::endl;
break;
}
case EE_ExpressivTrainingFailed:
{
os << std::endl << "Expressiv training for user " << userID << " FAILED!" << std::endl;
break;
}
case EE_ExpressivTrainingCompleted:
{
os << std::endl << "Expressiv training for user " << userID << " COMPLETED!" << std::endl;
break;
}
case EE_ExpressivTrainingDataErased:
{
os << std::endl << "Expressiv training data for user " << userID << " ERASED!" << std::endl;
break;
}
case EE_ExpressivTrainingRejected:
{
os << std::endl << "Expressiv training for user " << userID << " REJECTED!" << std::endl;
break;
}
case EE_ExpressivTrainingReset:
{
os << std::endl << "Expressiv training for user " << userID << " RESET!" << std::endl;
break;
}
case EE_ExpressivNoEvent:
default:
//## unhandled case
assert(0);
return;
}
promptUser();
}
bool handleUserInput() {
static std::string inputBuffer;
char c = getch();
if ((int)c == 10) {
std::cout << std::endl;
std::string command;
const size_t len = inputBuffer.length();
command.reserve(len);
// Convert the input to lower case first
for (size_t i=0; i < len; i++) {
command.append(1, tolower(inputBuffer.at(i)));
}
inputBuffer.clear();
bool success = parseCommand(command, std::cout);
promptUser();
return success;
}
else {
if ((int)c == 127) { // Backspace key
if (inputBuffer.length()) {
putchar('\b');
putchar(' ');
putchar('\b');
inputBuffer.erase(inputBuffer.end()-1);
}
}
else {
std::cout << c;
if(((int) c == 32) || ((int)c == 95) || (((int)c >= 97 && (int)c <=122)) || (((int)c >=48 && (int)c <= 57)))
inputBuffer.append(1,c);
}
}
return true;
}
void promptUser()
{
std::cout << "ExpressivDemo> "<<std::endl;
}
void nonblocking()
{
tcgetattr(0, &initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
//new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 0;
new_settings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_settings);
}
int getch()
{
int r;
unsigned char c=0;
if((r = read(0, &c, sizeof(c))) < 0 )
{
return r;
}
else
{
return c;
}
}
int createSocket()
{
struct sockaddr_in si_other;
int s, slen = sizeof(si_other);
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
std::cout<<"socket"<<std::endl;
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(startSendPort);
if (inet_aton("127.0.0.1", &si_other.sin_addr)==0)
{
std::cout<<"intet_aton failed !"<<std::endl;
//exit(1);
}
if(connect(s,(sockaddr*)&si_other,slen)==-1)
{
std::cout<<"connect failed !"<<std::endl;
// exit(1);
}
return s;
}
Your code is too big to be checked wholly. If you can reduce it to a SSCCE it will be easier for everybody.
Anyway, skimming over your code, only one thing catched my eye:
char *buf;
QString nStr = QString::number(n);
buf = nStr.toLocal8Bit().data();
return std::string(buf);
The call to toLocal8Bit() returns a temporary QByteArray and then you call its data() member function. The returned pointer is only valid during the lifetime of that temporary, that is, until the ending ;. After that the buf pointer is invalid and the next line renders undefined behavior.
You should do something like:
QString nStr = QString::number(n);
QByteArray a(nStr.toLocal8Bit());
char *buf = a.data();
return std::string(buf);
Or even better:
QString nStr = QString::number(n);
return std::string(nStr.toLocal8Bit().data());
Or just for show:
return std::string(QString::number(n).toLocal8Bit().data());