moved Interfaces to types.h, tooltip hiding fixed

This commit is contained in:
John Preston 2016-02-07 21:58:42 +03:00
parent 0ede06700a
commit 47ead03925
12 changed files with 312 additions and 251 deletions

View File

@ -315,15 +315,16 @@ namespace Sandbox {
t_assert_full(SandboxData != 0, "_data is null in Global::" #Name, __FILE__, __LINE__); \
return SandboxData->Name; \
}
#define DefineSandbox(Type, Name) DefineSandboxReadOnly(Type, Name) \
void Set##Name(const Type &Name) { \
t_assert_full(SandboxData != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \
SandboxData->Name = Name; \
} \
#define DefineSandboxRef(Type, Name) DefineSandboxReadOnly(Type, Name) \
Type &Ref##Name() { \
t_assert_full(SandboxData != 0, "_data is null in Global::Ref" #Name, __FILE__, __LINE__); \
return SandboxData->Name; \
}
#define DefineSandbox(Type, Name) DefineSandboxRef(Type, Name) \
void Set##Name(const Type &Name) { \
t_assert_full(SandboxData != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \
SandboxData->Name = Name; \
}
DefineSandboxReadOnly(uint64, LaunchId);

View File

@ -123,9 +123,10 @@ namespace Sandbox {
void finish();
#define DeclareSandboxReadOnly(Type, Name) const Type &Name();
#define DeclareSandbox(Type, Name) DeclareSandboxReadOnly(Type, Name) \
void Set##Name(const Type &Name); \
#define DeclareSandboxRef(Type, Name) DeclareSandboxReadOnly(Type, Name) \
Type &Ref##Name();
#define DeclareSandbox(Type, Name) DeclareSandboxRef(Type, Name) \
void Set##Name(const Type &Name);
DeclareSandboxReadOnly(uint64, LaunchId);

View File

@ -523,22 +523,33 @@ PopupMenu::~PopupMenu() {
PopupTooltip *PopupTooltipInstance = 0;
PopupTooltip::PopupTooltip(const QPoint &p, const QString &text, const style::Tooltip &st) : TWidget(0)
, _st(0) {
if (PopupTooltip *instance = PopupTooltipInstance) {
hide();
deleteLater();
} else {
PopupTooltipInstance = this;
Sandboxer::installEventFilter(this);
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
AbstractTooltipShower::~AbstractTooltipShower() {
if (PopupTooltipInstance && PopupTooltipInstance->_shower == this) {
PopupTooltipInstance->_shower = 0;
}
}
PopupTooltip::PopupTooltip() : TWidget(0)
, _shower(0)
, _st(0) {
PopupTooltipInstance = this;
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
PopupTooltipInstance->popup(p, text, &st);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));
}
void PopupTooltip::onShow() {
if (_shower) {
QString text = _shower->tooltipText();
if (text.isEmpty()) {
Hide();
} else {
PopupTooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
}
}
}
bool PopupTooltip::eventFilter(QObject *o, QEvent *e) {
@ -565,6 +576,13 @@ PopupTooltip::~PopupTooltip() {
}
void PopupTooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
Sandboxer::installEventFilter(this);
}
_point = m;
_st = st;
_text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true);
@ -631,14 +649,27 @@ void PopupTooltip::paintEvent(QPaintEvent *e) {
void PopupTooltip::hideEvent(QHideEvent *e) {
if (PopupTooltipInstance == this) {
PopupTooltipInstance = 0;
deleteLater();
Hide();
}
}
void PopupTooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
if (!PopupTooltipInstance) {
new PopupTooltip();
}
PopupTooltipInstance->_shower = shower;
if (delay >= 0) {
PopupTooltipInstance->_showTimer.start(delay);
} else {
PopupTooltipInstance->onShow();
}
}
void PopupTooltip::Hide() {
if (PopupTooltip *instance = PopupTooltipInstance) {
PopupTooltipInstance = 0;
instance->_showTimer.stop();
instance->_hideByLeaveTimer.stop();
instance->hide();
instance->deleteLater();
}

View File

@ -108,21 +108,31 @@ private:
};
class AbstractTooltipShower {
public:
virtual QString tooltipText() const = 0;
virtual QPoint tooltipPos() const = 0;
virtual const style::Tooltip *tooltipSt() const {
return &st::defaultTooltip;
}
virtual ~AbstractTooltipShower();
};
class PopupTooltip : public TWidget {
Q_OBJECT
public:
PopupTooltip(const QPoint &p, const QString &text, const style::Tooltip &st = st::defaultTooltip);
bool eventFilter(QObject *o, QEvent *e);
static void Show(int32 delay, const AbstractTooltipShower *shower);
static void Hide();
~PopupTooltip();
public slots:
void onShow();
void onHideByLeave();
protected:
@ -132,8 +142,14 @@ protected:
private:
PopupTooltip();
void popup(const QPoint &p, const QString &text, const style::Tooltip *st);
friend class AbstractTooltipShower;
const AbstractTooltipShower *_shower;
QTimer _showTimer;
Text _text;
QPoint _point;

View File

@ -75,8 +75,6 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, His
, _menu(0) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
_tooltipTimer.setSingleShot(true);
connect(&_tooltipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip()));
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
@ -1628,12 +1626,12 @@ void HistoryInner::onUpdateSelected() {
}
}
}
if (lnk || cursorState == HistoryInDateCursorState) {
_tooltipTimer.start(1000);
}
if (_dragCursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
PopupTooltip::Hide();
}
if (lnk || cursorState == HistoryInDateCursorState) {
PopupTooltip::Show(1000, this);
}
if (_dragAction == NoDrag) {
_dragCursorState = cursorState;
@ -1869,15 +1867,20 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
}
}
void HistoryInner::showLinkTip() {
QString HistoryInner::tooltipText() const {
TextLinkPtr lnk = textlnkOver();
if (lnk && !lnk->fullDisplayed()) {
new PopupTooltip(_dragPos, lnk->readable());
return lnk->readable();
} else if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) {
if (App::hoveredItem()) {
new PopupTooltip(_dragPos, App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
return App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat));
}
}
return QString();
}
QPoint HistoryInner::tooltipPos() const {
return _dragPos;
}
void HistoryInner::onParentGeometryChanged() {
@ -2017,9 +2020,6 @@ BotKeyboard::BotKeyboard() : TWidget()
setGeometry(0, 0, _st->margin, _st->margin);
_height = _st->margin;
setMouseTracking(true);
_cmdTipTimer.setSingleShot(true);
connect(&_cmdTipTimer, SIGNAL(timeout()), this, SLOT(showCommandTip()));
}
void BotKeyboard::paintEvent(QPaintEvent *e) {
@ -2259,17 +2259,22 @@ void BotKeyboard::clearSelection() {
}
}
void BotKeyboard::showCommandTip() {
QPoint BotKeyboard::tooltipPos() const {
return _lastMousePos;
}
QString BotKeyboard::tooltipText() const {
if (_sel >= 0) {
int row = (_sel / MatrixRowShift), col = _sel % MatrixRowShift;
if (!_btns.at(row).at(col).full) {
new PopupTooltip(_lastMousePos, _btns.at(row).at(col).cmd);
return _btns.at(row).at(col).cmd;
}
}
return QString();
}
void BotKeyboard::updateSelected() {
_cmdTipTimer.start(1000);
PopupTooltip::Show(1000, this);
if (_down >= 0) return;

View File

@ -33,7 +33,7 @@ enum DragState {
};
class HistoryWidget;
class HistoryInner : public TWidget {
class HistoryInner : public TWidget, public AbstractTooltipShower {
Q_OBJECT
public:
@ -97,6 +97,10 @@ public:
void notifyIsBotChanged();
void notifyMigrateUpdated();
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
~HistoryInner();
public slots:
@ -104,8 +108,6 @@ public slots:
void onUpdateSelected();
void onParentGeometryChanged();
void showLinkTip();
void openContextUrl();
void copyContextUrl();
void saveContextImage();
@ -150,8 +152,6 @@ private:
bool _firstLoading;
QTimer _tooltipTimer;
Qt::CursorShape _cursor;
typedef QMap<HistoryItem*, uint32> SelectedItems;
SelectedItems _selected;
@ -249,7 +249,7 @@ private:
};
class BotKeyboard : public TWidget {
class BotKeyboard : public TWidget, public AbstractTooltipShower {
Q_OBJECT
public:
@ -277,9 +277,12 @@ public:
return _wasForMsgId;
}
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
public slots:
void showCommandTip();
void updateSelected();
private:
@ -290,7 +293,6 @@ private:
FullMsgId _wasForMsgId;
int32 _height, _maxOuterHeight;
bool _maximizeSize, _singleUse, _forceReply;
QTimer _cmdTipTimer;
QPoint _lastMousePos;
struct Button {

View File

@ -91,8 +91,6 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD
App::contextItem(0);
_linkTipTimer.setSingleShot(true);
connect(&_linkTipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip()));
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
@ -1024,7 +1022,7 @@ void OverviewInner::onUpdateSelected() {
_cursorState = cursorState;
}
if (lnk || cursorState == HistoryInDateCursorState) {
_linkTipTimer.start(1000);
PopupTooltip::Show(1000, this);
}
fixItemIndex(_dragItemIndex, _dragItem);
@ -1139,16 +1137,20 @@ void OverviewInner::onUpdateSelected() {
}
}
QPoint OverviewInner::tooltipPos() const {
return _dragPos;
}
void OverviewInner::showLinkTip() {
QString OverviewInner::tooltipText() const {
TextLinkPtr lnk = textlnkOver();
if (lnk && !lnk->fullDisplayed()) {
new PopupTooltip(_dragPos, lnk->readable());
return lnk->readable();
} else if (_cursorState == HistoryInDateCursorState && _dragAction == NoDrag && _mousedItem) {
if (HistoryItem *item = App::histItemById(itemChannel(_mousedItem), itemMsgId(_mousedItem))) {
new PopupTooltip(_dragPos, item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
return item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat));
}
}
return QString();
}
void OverviewInner::updateDragSelection(MsgId dragSelFrom, int32 dragSelFromIndex, MsgId dragSelTo, int32 dragSelToIndex, bool dragSelecting) {

View File

@ -21,7 +21,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#pragma once
class OverviewWidget;
class OverviewInner : public QWidget, public RPCSender {
class OverviewInner : public QWidget, public AbstractTooltipShower, public RPCSender {
Q_OBJECT
public:
@ -76,12 +76,15 @@ public:
void clearSelectedItems(bool onlyTextSelection = false);
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
~OverviewInner();
public slots:
void onUpdateSelected();
void showLinkTip();
void openContextUrl();
void copyContextUrl();

View File

@ -34,37 +34,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "audio.h"
#include "localstorage.h"
class InterfacesMetadatasMap : public QMap<uint64, InterfacesMetadata*> {
public:
~InterfacesMetadatasMap() {
for (const_iterator i = cbegin(), e = cend(); i != e; ++i) {
delete i.value();
}
}
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask) {
typedef QMap<uint64, InterfacesMetadata*> InterfacesMetadatasMap;
static InterfacesMetadatasMap InterfacesMetadatas;
static QMutex InterfacesMetadatasMutex;
QMutexLocker lock(&InterfacesMetadatasMutex);
InterfacesMetadatasMap::const_iterator i = InterfacesMetadatas.constFind(mask);
if (i == InterfacesMetadatas.cend()) {
InterfacesMetadata *meta = new InterfacesMetadata(mask);
if (!meta) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
i = InterfacesMetadatas.insert(mask, meta);
}
return i.value();
}
InterfaceWrapStruct InterfaceWraps[64];
QAtomicInt InterfaceIndexLast(0);
namespace {
int32 peerColorIndex(const PeerId &peer) {
int32 myId(MTP::authedId()), peerId(peerToBareInt(peer));

View File

@ -20,174 +20,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
*/
#pragma once
class Interfaces;
typedef void(*InterfaceConstruct)(void *location, Interfaces *interfaces);
typedef void(*InterfaceDestruct)(void *location);
struct InterfaceWrapStruct {
InterfaceWrapStruct() : Size(0), Construct(0), Destruct(0) {
}
InterfaceWrapStruct(int size, InterfaceConstruct construct, InterfaceDestruct destruct)
: Size(size)
, Construct(construct)
, Destruct(destruct) {
}
int Size;
InterfaceConstruct Construct;
InterfaceDestruct Destruct;
};
template <int Value, int Denominator>
struct CeilDivideMinimumOne {
static const int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0));
};
template <typename Type>
struct InterfaceWrapTemplate {
static const int Size = CeilDivideMinimumOne<sizeof(Type), sizeof(uint64)>::Result * sizeof(uint64);
static void Construct(void *location, Interfaces *interfaces) {
new (location) Type(interfaces);
}
static void Destruct(void *location) {
((Type*)location)->~Type();
}
};
extern InterfaceWrapStruct InterfaceWraps[64];
extern QAtomicInt InterfaceIndexLast;
template <typename Type>
class BasicInterface {
public:
static int Index() {
static QAtomicInt _index(0);
if (int index = _index.loadAcquire()) {
return index - 1;
}
while (true) {
int last = InterfaceIndexLast.loadAcquire();
if (InterfaceIndexLast.testAndSetOrdered(last, last + 1)) {
t_assert(last < 64);
if (_index.testAndSetOrdered(0, last + 1)) {
InterfaceWraps[last] = InterfaceWrapStruct(InterfaceWrapTemplate<Type>::Size, InterfaceWrapTemplate<Type>::Construct, InterfaceWrapTemplate<Type>::Destruct);
}
break;
}
}
return _index.loadAcquire() - 1;
}
static uint64 Bit() {
return (1 << Index());
}
};
template <typename Type>
class BasicInterfaceWithPointer : public BasicInterface<Type> {
public:
BasicInterfaceWithPointer(Interfaces *interfaces) : interfaces(interfaces) {
}
Interfaces *interfaces = 0;
};
class InterfacesMetadata {
public:
InterfacesMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
for (int i = 0; i < 64; ++i) {
uint64 m = (1 << i);
if (_mask & m) {
int s = InterfaceWraps[i].Size;
if (s) {
offsets[i] = size;
size += s;
} else {
offsets[i] = -1;
}
} else if (_mask < m) {
last = i;
for (; i < 64; ++i) {
offsets[i] = -1;
}
} else {
offsets[i] = -1;
}
}
}
int size, last;
int offsets[64];
private:
uint64 _mask;
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask);
class Interfaces {
public:
Interfaces(uint64 mask = 0) : _meta(GetInterfacesMetadata(mask)), _data(0) {
if (_meta->size) {
_data = malloc(_meta->size);
if (!_data) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
try {
InterfaceWraps[i].Construct(_dataptrunsafe(offset), this);
} catch (...) {
while (i > 0) {
--i;
offset = _meta->offsets[--i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
throw;
}
}
}
}
}
~Interfaces() {
if (_data) {
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
free(_data);
}
}
template <typename Type>
Type *Get() {
return (Type*)_dataptr(_meta->offsets[Type::Index()]);
}
template <typename Type>
const Type *Get() const {
return (const Type*)_dataptr(_meta->offsets[Type::Index()]);
}
private:
void *_dataptrunsafe(int skip) const {
return (char*)_data + skip;
}
void *_dataptr(int skip) const {
return (skip >= 0) ? _dataptrunsafe(skip) : 0;
}
const InterfacesMetadata *_meta;
void *_data;
};
typedef int32 ChannelId;
static const ChannelId NoChannel = 0;

View File

@ -1016,3 +1016,34 @@ MimeType mimeTypeForData(const QByteArray &data) {
}
return MimeType(QMimeDatabase().mimeTypeForData(data));
}
class InterfacesMetadatasMap : public QMap<uint64, InterfacesMetadata*> {
public:
~InterfacesMetadatasMap() {
for (const_iterator i = cbegin(), e = cend(); i != e; ++i) {
delete i.value();
}
}
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask) {
typedef QMap<uint64, InterfacesMetadata*> InterfacesMetadatasMap;
static InterfacesMetadatasMap InterfacesMetadatas;
static QMutex InterfacesMetadatasMutex;
QMutexLocker lock(&InterfacesMetadatasMutex);
InterfacesMetadatasMap::const_iterator i = InterfacesMetadatas.constFind(mask);
if (i == InterfacesMetadatas.cend()) {
InterfacesMetadata *meta = new InterfacesMetadata(mask);
if (!meta) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
i = InterfacesMetadatas.insert(mask, meta);
}
return i.value();
}
InterfaceWrapStruct InterfaceWraps[64];
QAtomicInt InterfaceIndexLast(0);

View File

@ -529,6 +529,174 @@ inline void destroyImplementation(I *&ptr) {
deleteAndMark(ptr);
}
class Interfaces;
typedef void(*InterfaceConstruct)(void *location, Interfaces *interfaces);
typedef void(*InterfaceDestruct)(void *location);
struct InterfaceWrapStruct {
InterfaceWrapStruct() : Size(0), Construct(0), Destruct(0) {
}
InterfaceWrapStruct(int size, InterfaceConstruct construct, InterfaceDestruct destruct)
: Size(size)
, Construct(construct)
, Destruct(destruct) {
}
int Size;
InterfaceConstruct Construct;
InterfaceDestruct Destruct;
};
template <int Value, int Denominator>
struct CeilDivideMinimumOne {
static const int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0));
};
template <typename Type>
struct InterfaceWrapTemplate {
static const int Size = CeilDivideMinimumOne<sizeof(Type), sizeof(uint64)>::Result * sizeof(uint64);
static void Construct(void *location, Interfaces *interfaces) {
new (location) Type(interfaces);
}
static void Destruct(void *location) {
((Type*)location)->~Type();
}
};
extern InterfaceWrapStruct InterfaceWraps[64];
extern QAtomicInt InterfaceIndexLast;
template <typename Type>
class BasicInterface {
public:
static int Index() {
static QAtomicInt _index(0);
if (int index = _index.loadAcquire()) {
return index - 1;
}
while (true) {
int last = InterfaceIndexLast.loadAcquire();
if (InterfaceIndexLast.testAndSetOrdered(last, last + 1)) {
t_assert(last < 64);
if (_index.testAndSetOrdered(0, last + 1)) {
InterfaceWraps[last] = InterfaceWrapStruct(InterfaceWrapTemplate<Type>::Size, InterfaceWrapTemplate<Type>::Construct, InterfaceWrapTemplate<Type>::Destruct);
}
break;
}
}
return _index.loadAcquire() - 1;
}
static uint64 Bit() {
return (1 << Index());
}
};
template <typename Type>
class BasicInterfaceWithPointer : public BasicInterface<Type> {
public:
BasicInterfaceWithPointer(Interfaces *interfaces) : interfaces(interfaces) {
}
Interfaces *interfaces = 0;
};
class InterfacesMetadata {
public:
InterfacesMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
for (int i = 0; i < 64; ++i) {
uint64 m = (1 << i);
if (_mask & m) {
int s = InterfaceWraps[i].Size;
if (s) {
offsets[i] = size;
size += s;
} else {
offsets[i] = -1;
}
} else if (_mask < m) {
last = i;
for (; i < 64; ++i) {
offsets[i] = -1;
}
} else {
offsets[i] = -1;
}
}
}
int size, last;
int offsets[64];
private:
uint64 _mask;
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask);
class Interfaces {
public:
Interfaces(uint64 mask = 0) : _meta(GetInterfacesMetadata(mask)), _data(0) {
if (_meta->size) {
_data = malloc(_meta->size);
if (!_data) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
try {
InterfaceWraps[i].Construct(_dataptrunsafe(offset), this);
} catch (...) {
while (i > 0) {
--i;
offset = _meta->offsets[--i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
throw;
}
}
}
}
}
~Interfaces() {
if (_data) {
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
free(_data);
}
}
template <typename Type>
Type *Get() {
return (Type*)_dataptr(_meta->offsets[Type::Index()]);
}
template <typename Type>
const Type *Get() const {
return (const Type*)_dataptr(_meta->offsets[Type::Index()]);
}
private:
void *_dataptrunsafe(int skip) const {
return (char*)_data + skip;
}
void *_dataptr(int skip) const {
return (skip >= 0) ? _dataptrunsafe(skip) : 0;
}
const InterfacesMetadata *_meta;
void *_data;
};
template <typename R>
class FunctionImplementation {
public: