Skip to content

Instantly share code, notes, and snippets.

@kou-yeung
Created November 18, 2017 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kou-yeung/212d252eef30dadc9505ba4477798cab to your computer and use it in GitHub Desktop.
Save kou-yeung/212d252eef30dadc9505ba4477798cab to your computer and use it in GitHub Desktop.
#include <memory> // for std::shared_ptr
#include <vector> // for std::vector
#include <map> // for std::map
#include <functional> // for std::function
#include <random> // for std::random_device , std::mt19937
class CardData
{
public:
// 本来は sqlite3_stmt* を受け取りますが、IDがわかればいいのでID簡易化しました
// CardData(sqlite3_stmt* stmt) {/* ... */}
CardData(uint32_t id):_id(id) {}
uint32_t _id; // カードID
};
using CardDataPtr = std::shared_ptr<CardData>;
using CardDatas = std::vector<CardDataPtr>;
class CardDataCache
{
public:
// id を受け取り、キャッシュされた CardDataPtr を返します。なければ nullptr を返す
CardDataPtr fetch(uint32_t id) const
{
auto it = cache.find(id);
return (it != cache.end()) ? it->second : nullptr;
}
// CardDataPtr を受け取り、キャッシュされてない場合キャッシュに登録する
CardDataPtr store(CardDataPtr cardData)
{
auto it = cache.find(cardData->_id);
if (it == cache.end()) cache.emplace(cardData->_id, cardData);
return cardData;
}
private:
std::map<uint32_t, CardDataPtr> cache; // キャッシュ : KEY( カードID ) VALUE(CardDataPtr)
};
class DatabaseManager
{
public:
static DatabaseManager* getInstance()
{
static DatabaseManager _instance;
return &_instance;
}
// 今回の不具合を再現するのが目的です
// 受け取るID一覧を呼び出しデータの順が保証しなければ再現できるため
// std::vector<uint32_t> を受け取り、順がランダムのCardDataPtr を渡すように変更しています。
// void query(const std::string& sql, std::function<void(sqlite3_stmt*)> cb)
void query(const std::vector<uint32_t>& ids, std::function<void(CardDataPtr)> cb) const
{
// ids をコピーしてシャッフルする
auto result = std::vector<uint32_t>(std::begin(ids), std::end(ids));
std::random_device seed_gen;
std::mt19937 engine(seed_gen());
std::shuffle(std::begin(result), std::end(result), engine);
// シャッフルしたID一覧で CardDataPtr を生成しラムダを呼び出す
for (const auto& id : result)
{
cb(std::make_shared<CardData>( id ));
}
}
};
#include <iostream>
#include "class.h"
class CardModel
{
public:
CardModel()
{
// 公式サイトから引用
//=======================
// ・表示に必要なカードIDリスト: 1, 2, 3, 4, 5
// ・メモリ上のカードIDリスト: 1, 2, "空白", 4, 5
//=======================
// とりあえず、[1,2,4,5] のデータをキャッシュさせれば 3 を取得時空白(nullptr)になります
_cardDataCache.store(std::make_shared<CardData>(1));
_cardDataCache.store(std::make_shared<CardData>(2));
_cardDataCache.store(std::make_shared<CardData>(4));
_cardDataCache.store(std::make_shared<CardData>(5));
}
// 実装は公式が提供されたソースコードをベースにしてインターフェースに合わせて調整しています
CardDatas getMasterCardDataByIds(const std::vector<uint32_t>& masterCardIds) const
{
std::vector<CardDataPtr> result;
result.resize(masterCardIds.size());
size_t exists = 0;
for (int i = 0; i < masterCardIds.size(); i++)
{
auto p = result[i] = _cardDataCache.fetch(masterCardIds[i]);
if (p != nullptr) {
exists++;
}
}
if (masterCardIds.size() == exists) {
return result;
}
// 今回のサンプルでは使用しないため、コメントアウトしました。form(...) join(...) の実装を省く!
// string sql = form("SELECT * FROM cache.cards where id IN (%s);", join(masterCardIds, ",").c_str());
int i = 0;
// 注 : SQL 文字列を受け取る代わりに、直接 ID一覧を受け取るようにしています
DatabaseManager::getInstance()->query(masterCardIds, [this, &result, &i](CardDataPtr cardData){
if (result[i] == nullptr) {
result[i] = _cardDataCache.store(cardData);
}
i++;
});
return result;
}
private:
mutable CardDataCache _cardDataCache;
};
int main()
{
auto model = CardModel{};
auto masterCardIds = std::vector<uint32_t>{ 1,2,3,4,5 };
auto cardDatas = model.getMasterCardDataByIds(masterCardIds);
for (const auto& card : cardDatas)
{
std::cout << card->_id << " ";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment