विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ Mixin इडियम

नमस्ते, मैं 光城 हूँ — आज आपके साथ C++那些事 का एक इडियम साझा करता हूँ: mixin

Mixins, Lisp की एक संकल्पना है। mixin किसी क्लास का एक भाग होता है — अर्थात् इसे अन्य क्लासों या mixinों के साथ संयोजित (compose) होने के लिए बनाया जाता है। सामान्य स्वतंत्र क्लास (जैसे Person) तथा mixin का अंतर यह है कि mixin कुछ छोटे-छोटे कार्य-खंडों (जैसे print या display) का अनुकरण करता है, और स्वतंत्र उपयोग के लिए नहीं होता; इसके विपरीत, इसे उन अन्य क्लासों (जैसे Person) के साथ जोड़ा जाना चाहिए जिन्हें वह कार्यक्षमता चाहिए।

अतः mixin का उद्देश्य बहु-इनहेरिटेंस (multiple inheritance) जैसी सुविधा देना है।

C++ में mixin की मूल लिखावट इस प्रकार है:

template <class Super>
class Mixin : public Super {
. /* mixin body */
};

अथवा

template<typename... Super>
class Mixin : public Super... {
  public:
    Mixin() :  Super...() {}
  // ...
};

नोट: इस अनुभाग का सारा उदाहरण कोड प्रीमियम कम्युनिटी से प्राप्त किया जा सकता है।

उदाहरण 1: scaling तथा rotation

मान लीजिए हमें किसी आयत/वर्ग पर scaling, rotation, border जोड़ना आदि करना है — ये सभी ऑपरेशन उसकी चौड़ाई तथा ऊँचाई को प्रभावित करते हैं; ऐसा काम हम mixin से कर सकते हैं।

सबसे पहले, हम वर्ग तथा आयत को mixin की बेस क्लास के रूप में परिभाषित कर सकते हैं।

// 正方形类
class Square : public Shape {
public:
    Square(int sideLength) : sideLength(sideLength) {}
    int GetWidth() const override { return sideLength; }
    int GetHeight() const override { return sideLength; }
private:
    int sideLength;
};

// 长方形类
class Rectangle : public Shape {
public:
    Rectangle(int width, int height) : width(width), height(height) {}
    int GetWidth() const override { return width; }
    int GetHeight() const override { return height; }
private:
    int width;
    int height;
};

फिर mixin सबक्लास से कार्यक्षमता विस्तारित करते हैं; उदाहरण के लिए: scaling के लिए हम एक कस्टम क्लास बना सकते हैं जिसकी चौड़ाई तथा ऊँचाई scale को संगत चौड़ाई-ऊँचाई से गुणा करने पर प्राप्त होती है।

// 缩放的Mixin
template <class Base, int ScaleX, int ScaleY>
class Scale : public Base {
 public:
  int GetScaledWidth() const { return Base::GetWidth() * ScaleX; }
  int GetScaledHeight() const { return Base::GetHeight() * ScaleY; }
};

ऐसे परिदृश्यों के लिए mixin का उपयोग बहुत उपयुक्त है।

उपयोग के समय:

using ModifiedShape = Border<RotateBy90<Scale<Square, 2, 1>>, 10>;

उदाहरण 2: redo तथा undo

पूर्ण उदाहरण redo तथा undo ऑपरेशन का अनुकरण करता है, जिससे हम डेटे के बीच आ-जा सकते हैं। Redoable<Undoable<Number>> — यहाँ हम इन क्लासों को एक साथ मिलाकर उपयोग कर सकते हैं।

#include <iostream>

using namespace std;

struct Number {
    typedef int value_type;
    int n;
    void set(int v) {
        n = v;
    }
    int get() const {
        return n;
    }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE {
    typedef T value_type;
    T before;
    void set(T v) {
        before = BASE::get();
        BASE::set(v);
    }
    void undo() {
        BASE::set(before);
    }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Redoable : public BASE {
    typedef T value_type;
    T after;
    void set(T v) {
        after = v;
        BASE::set(v);
    }
    void redo() {
        BASE::set(after);
    }
};

typedef Redoable<Undoable<Number>> ReUndoableNumber;

int main() {
    ReUndoableNumber mynum;
    mynum.set(42);
    mynum.set(84);
    cout << mynum.get() << '\n';  // Output: 84
    mynum.undo();
    cout << mynum.get() << '\n';  // Output: 42
    mynum.redo();
    cout << mynum.get() << '\n';  // Output: back to 84
}

उदाहरण 3: बार-बार प्रिंट (repeat print)

ब्लॉक जोड़ने (building blocks) वाला एक और उदाहरण लेते हैं: बार-बार प्रिंट।

सबसे पहले एक ऐसी क्लास है जिससे हम व्यक्ति का नाम print कर सकते हैं।

class Name
{
public:
    Name(std::string firstName, std::string lastName)
      : firstName_(std::move(firstName))
      , lastName_(std::move(lastName)) {}

    void print() const
    {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }

private:
    std::string firstName_;
    std::string lastName_;
};

तो यदि मैं बार-बार प्रिंट करना चाहूँ तो?

हम इसे ऐसे कर सकते हैं — mixin का उपयोग करके Name क्लास को उसके भीतर पास कर दें।

template<typename Printable>
struct RepeatPrint : Printable
{
    explicit RepeatPrint(Printable const& printable) : Printable(printable) {}
    void repeat(unsigned int n) const
    {
        while (n-- > 0)
        {
            this->print();
        }
    }
};

template<typename Printable>
RepeatPrint<Printable> repeatPrint(Printable const& printable)
{
    return RepeatPrint<Printable>(printable);
}

उपयोग:

Name ned("Eddard", "Stark");    
repeatPrint(ned).repeat(10);

इस समय n बार दोहराया गया प्रिंट परिणाम प्राप्त होता है।

तो mixin किन परिदृश्यों के लिए सर्वाधिक उपयुक्त है?

  • मूल क्लास को यथावत रखना,
  • क्लाइंट कोड मूल क्लास का सीधा उपयोग नहीं करता — उन्नत कार्यक्षमता के उपयोग के लिए उसे mixin में लपेटना पड़ता है,

स्टैंडर्ड लाइब्रेरी

स्टैंडर्ड लाइब्रेरी में mixin तकनीक का एक उपयोग है: std::nested_exception।

std::nested_exception एक बहुरूपी (polymorphic) mixin क्लास है, जो वर्तमान अपवाद (exception) को पकड़कर संग्रहीत कर सकता है, जिससे एक-दूसरे के भीतर किसी भी टाइप के अपवाद नेस्ट किए जा सकते हैं।

template <typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{}

सोर्स कोड में हम देख सकते हैं कि nested_exception को mixin की बेस क्लास के रूप में उपयोग किया गया है।

संदर्भ

https://cedanet.com.au/ceda/Xc++/$mixin/mixins-in-straight-c++.php

https://www.fluentcpp.com/2017/12/12/mixin-classes-yang-crtp/

https://stackoverflow.com/questions/18773367/what-are-mixins-as-a-concept

संबंधित विषय

टिप्पणियाँ