সি রাষ্ট্র-মেশিন নকশা [বন্ধ]


193

আমি মিশ্রিত সি এবং সি ++ এ একটি ছোট প্রকল্প তৈরি করছি। আমি আমার এক কর্মী থ্রেডের হৃদয়ে একটি ছোট-ইশ স্টেট-মেশিন তৈরি করছি।

আমি ভাবছিলাম যে আপনি যদি এসও তে গুরু আপনার রাজ্য-মেশিন ডিজাইন কৌশলগুলি ভাগ করে নেন।

দ্রষ্টব্য: আমি প্রাথমিকভাবে চেষ্টা ও পরীক্ষামূলক বাস্তবায়ন কৌশলগুলির পরে আছি।

আপডেট হয়েছে: এসও তে জড়ো করা সমস্ত দুর্দান্ত ইনপুটের ভিত্তিতে, আমি এই আর্কিটেকচারে স্থির হয়েছি:

একটি ইভেন্ট পাম্প একটি ইভেন্ট ইন্টিগ্রেটারকে নির্দেশ করে যা একটি প্রেরককে নির্দেশ করে।  প্রেরকটি n ক্রিয়াগুলির মাধ্যমে 1 এ নির্দেশ করে যা ইভেন্ট ইন্টিগ্রেটারের দিকে ফিরে নির্দেশ করে।  ওয়াইল্ডকার্ড সহ একটি রূপান্তর টেবিল প্রেরককে নির্দেশ করে।


4
উত্তর এখানে খুব ভাল। এছাড়াও এই সদৃশ প্রশ্নটি দেখুন যার বেশ কয়েকটি ভাল উত্তর রয়েছে: স্ট্যাকওভারফ্লো
মাইকেল



উত্তর:


170

যে স্টেট মেশিনগুলি আমি এর আগে ডিজাইন করেছি (সি, সি ++ নয়) সেগুলি একটি structঅ্যারে এবং লুপে নেমে এসেছে । কাঠামোটি মূলত একটি রাষ্ট্র এবং ইভেন্ট (চেহারা দেখার জন্য) এবং এমন একটি ফাংশন নিয়ে গঠিত যা নতুন রাষ্ট্রকে ফিরিয়ে দেয়, এরকম কিছু:

typedef struct {
    int st;
    int ev;
    int (*fn)(void);
} tTransition;

তারপরে আপনি সরল সংজ্ঞা দিয়ে আপনার রাজ্য এবং ইভেন্টগুলি সংজ্ঞায়িত করেন ( ANYসেগুলি বিশেষ চিহ্নিতকারী, নীচে দেখুন):

#define ST_ANY              -1
#define ST_INIT              0
#define ST_ERROR             1
#define ST_TERM              2
: :
#define EV_ANY              -1
#define EV_KEYPRESS       5000
#define EV_MOUSEMOVE      5001

তারপরে আপনি সমস্ত ক্রিয়াকলাপ সংজ্ঞায়িত করে যা ট্রানজিশন দ্বারা ডাকা হয়:

static int GotKey (void) { ... };
static int FsmError (void) { ... };

এই সমস্ত ফাংশনটি কোনও ভেরিয়েবল না নেওয়ার জন্য এবং স্টেট মেশিনের জন্য নতুন রাষ্ট্র ফিরিয়ে দেওয়ার জন্য লেখা। এই উদাহরণে গ্লোবাল ভেরিয়েবলগুলি প্রয়োজনীয় যেখানে রাষ্ট্রীয় কার্যাদিগুলিতে কোনও তথ্য প্রেরণের জন্য ব্যবহৃত হয়।

গ্লোবালগুলি ব্যবহার করা যেমন শোনা যায় ততটা খারাপ নয় যেহেতু এফএসএম সাধারণত একটি সংকলন ইউনিটের অভ্যন্তরে লক থাকে এবং সমস্ত ভেরিয়েবলগুলি সেই ইউনিটের সাথে স্থির থাকে (এই কারণেই আমি উপরে "গ্লোবাল" এর কাছাকাছি উদ্ধৃতি ব্যবহার করেছি - সেগুলি আরও বেশি ভাগ করা হয়েছে সত্যিকারের বিশ্বব্যাপী থেকে এফএসএম)। সমস্ত গ্লোবালগুলির মতো এটিরও যত্ন নেওয়া দরকার।

ট্রানজিশন অ্যারে তারপরে সমস্ত সম্ভাব্য ট্রানজিশন এবং সেই ক্রিয়াগুলির জন্য ডেকে আনা ফাংশনগুলি ব্যাখ্যা করে (ক্যাশে-সমস্ত শেষটি সহ):

tTransition trans[] = {
    { ST_INIT, EV_KEYPRESS, &GotKey},
    : :
    { ST_ANY, EV_ANY, &FsmError}
};
#define TRANS_COUNT (sizeof(trans)/sizeof(*trans))

এর অর্থ হ'ল: আপনি যদি ST_INITরাজ্যে থাকেন এবং আপনি EV_KEYPRESSইভেন্টটি পান তবে একটি কল করুন GotKey

FSM এর কাজগুলি তখন তুলনামূলকভাবে সহজ লুপে পরিণত হয়:

state = ST_INIT;
while (state != ST_TERM) {
    event = GetNextEvent();
    for (i = 0; i < TRANS_COUNT; i++) {
        if ((state == trans[i].st) || (ST_ANY == trans[i].st)) {
            if ((event == trans[i].ev) || (EV_ANY == trans[i].ev)) {
                state = (trans[i].fn)();
                break;
            }
        }
    }
}

উপরে বর্ণিত ST_ANYহিসাবে, ওয়াইল্ড কার্ড হিসাবে ব্যবহারের বিষয়টি নোট করুন , কোনও ইভেন্টের বর্তমান অবস্থা নির্বিশেষে কোনও ক্রিয়াকলাপ কল করার অনুমতি দেয়। EV_ANYএছাড়াও একইভাবে কাজ করে, একটি নির্দিষ্ট রাজ্যে কোনও ইভেন্টকে কোনও ফাংশন কল করার অনুমতি দেয়।

এটি গ্যারান্টিও দিতে পারে যে, আপনি যদি রূপান্তরের অ্যারে শেষে পৌঁছে থাকেন তবে আপনার এফএসএম সঠিকভাবে নির্মিত হয়নি বলে উল্লেখ করে একটি ত্রুটি পাবেন ( ST_ANY/EV_ANYসংমিশ্রণটি ব্যবহার করে )।

এম্বেড থাকা সিস্টেমগুলির জন্য যোগাযোগ স্ট্যাক এবং প্রোটোকলের প্রাথমিক প্রয়োগ হিসাবে আমি একটি দুর্দান্ত অনেক যোগাযোগ প্রকল্পে এর জন্য কোডটির অনুরূপ কোড ব্যবহার করেছি। বড় সুবিধাটি হ'ল রূপান্তর অ্যারে পরিবর্তনের ক্ষেত্রে এর সরলতা এবং আপেক্ষিক স্বাচ্ছন্দ্য ছিল।

আমার সন্দেহ নেই যে এখানে উচ্চ-স্তরের বিমূর্ততা থাকবে যা আজকাল আরও উপযুক্ত হতে পারে তবে আমি সন্দেহ করি যে তারা সকলেই এই একই ধরণের কাঠামোটিতে ফোটে।


এবং, ldogএকটি মন্তব্যে যেমন বলা হয়েছে, আপনি সমস্ত ফাংশনে স্ট্রাকচার পয়েন্টারটি (এবং ইভেন্ট লুপটিতে এটি ব্যবহার করে) পুরোপুরি গ্লোবালগুলি এড়াতে পারবেন can এটি একাধিক রাষ্ট্রের মেশিনকে হস্তক্ষেপ ছাড়াই পাশাপাশি পাশাপাশি চালানোর অনুমতি দেবে।

কেবল একটি কাঠামোর ধরণ তৈরি করুন যা মেশিন-নির্দিষ্ট ডেটা ধারণ করে (সর্বাধিক ন্যূনতম সময়ে স্টেট) এবং গ্লোবালের পরিবর্তে এটি ব্যবহার করুন।

যে কারণ আমি খুব কমই করেছি তা হ'ল কারণ আমি লিখেছি বেশিরভাগ রাষ্ট্রীয় মেশিনগুলি সিঙ্গলটন প্রকারের ছিল (উদাহরণস্বরূপ এক-অফ, প্রক্রিয়া-সূচনা, কনফিগারেশন ফাইল রিডিং), একাধিক দৌড়ানোর দরকার নেই not । আপনার যদি একের বেশি চালানো দরকার হয় তবে এর মান রয়েছে।


24
একটি বিশালাকার সুইচ এফএসএম এর সাথে কোড মিশ্রিত করে। এমনকি ট্রানজিশনের জন্য কেবলমাত্র একটি ফাংশন কল থাকলেও এখনও কিছু কোড রয়েছে এবং কেবলমাত্র একটি 4-লাইনের ছোট ট্রানজিশনের ইনলাইন যুক্ত করে কারও পক্ষে এটি আপত্তি করা সহজ। মুরগি দশ লাইন এক। তারপরে হাতছাড়া হয়ে যায়। স্ট্রাক্ট অ্যারের সাথে, এফএসএম পরিষ্কার থাকে - আপনি প্রতিটি রূপান্তর এবং প্রভাব (ফাংশন) দেখতে পারেন। এবং আমি তখন শুরু করেছি যখন
এনামগুলি আইএসওর

5
আপনি ঠিক বলেছেন, এনামগুলি আরও ভাল হবে তবে আমি এখনও স্ট্রাক্ট অ্যারে হিসাবে এফএসএমকে পছন্দ করি। তারপরে এগুলি কোডের পরিবর্তে ডেটা দ্বারা চালিত হয় (ভাল, কিছু কোড আছে তবে আমি যে এফএসএম লুপ দিয়েছি তা পূরণ করার সুযোগ স্লিম)।
paxdiablo

2
এটি ভাল, প্রক্রিয়া কনট্রোল স্টেট মেশিনগুলির জন্য আমি সর্বদা প্রতিটি রাজ্যের জন্য তিনটি (সম্ভবত খালি) সাবস্টেট যুক্ত করতাম, যাতে একটি রাষ্ট্রীয় ফাংশনের জন্য কলটি গটকি (সাবস্টেট) হয়ে যায়, যেখানে সাবস্টেটটি হবে: - এসএস_এনটিআরআই - এসএস_আরুন - এসএস_এক্সআইআইটি মূলত, রাষ্ট্রের ফাংশনটি প্রবেশের সময় একটি SS_ENTRY সাবস্টেটের সাথে কল হয়, যাতে রাজ্য কোনও স্ট্যাটাস পুনর্নির্মাণ করতে পারে (উদাহরণস্বরূপ অ্যাকুয়েটরের অবস্থান)। যখন কোনও স্থানান্তর নেই, এসএস_আরুন সাবস্টেট মানটি পাস হয়ে যায়। স্থানান্তরিত হওয়ার পরে, রাষ্ট্রের ফাংশনটি এসএস_এক্সআইটি সাবস্টেটের সাথে কল করা হয়, যাতে এটি কোনও ক্লিনআপ (উদাহরণস্বরূপ ডিএলোকট রিসোর্স) করতে পারে।
মেটিউ

13
আপনি উল্লেখ করেছেন যে আপনি গ্লোবালগুলি ব্যবহার করে ডেটা ভাগ করেন, তবে আপনি যদি রাষ্ট্রীয় ক্রিয়াকলাপগুলি int (*fn)(void*);যেখানে void*প্রতিটি রাজ্য ফাংশন প্যারামিটার হিসাবে গ্রহণ করে সেই ডেটারের পয়েন্টারটি কোথায় থাকে তা নির্ধারণ করে তবে এটি সম্ভবত পরিষ্কার হবে । তারপরে রাষ্ট্রীয় ফাংশন হয় ডেটা ব্যবহার করতে পারে বা তাদের উপেক্ষা করতে পারে।
ldog

13
আমি এফএসএম লেখার জন্য একই ডেটা / কোড বিচ্ছেদ ব্যবহার করি, তা ছাড়া 'ওয়াইল্ডকার্ড' রাষ্ট্রের সূচনা করার বিষয়টি আমার কাছে কখনও ঘটেনি। আকর্ষণীয় ধারণা! যাইহোক, আপনার অনেকগুলি রাজ্য (সি কোড স্বয়ংক্রিয়ভাবে উত্পন্ন হওয়ার পরে আমার ক্ষেত্রে এটিই ছিল) যদি রূপান্তরের অ্যারের পুনরাবৃত্তি ব্যয়বহুল হয়ে উঠতে পারে। এই ধরনের পরিস্থিতিতে, প্রতি রাজ্যে এক ধরণের ট্রানজিশন থাকা আরও দক্ষ হবে। সুতরাং একটি রাষ্ট্র আর একটি এনাম মান নয়, তবে একটি রূপান্তর টেবিল। এইভাবে, আপনাকে মেশিনে সমস্ত ট্রানজিশনগুলি নিয়ে পুনরাবৃত্তি করতে হবে না তবে কেবলমাত্র বর্তমান অবস্থার সাথে প্রাসঙ্গিক।
ফ্রেরিচ রাবাবে

78

অন্যান্য উত্তরগুলি ভাল, তবে যখন রাষ্ট্রীয় মেশিনটি খুব সাধারণ হয় তখন আমি একটি খুব "হালকা" প্রয়োগ ব্যবহার করি যা দেখে মনে হয়:

enum state { ST_NEW, ST_OPEN, ST_SHIFT, ST_END };

enum state current_state = ST_NEW;

while (current_state != ST_END)
{
    input = get_input();

    switch (current_state)
    {
        case ST_NEW:
        /* Do something with input and set current_state */
        break;

        case ST_OPEN:
        /* Do something different and set current_state */
        break;

        /* ... etc ... */
    }
}

আমি যখন এটি ব্যবহার করব তখন রাষ্ট্রীয় মেশিনটি যথেষ্ট সহজ যে ফাংশন পয়েন্টার এবং রাষ্ট্রের রূপান্তর সারণী পদ্ধতির ওভারকিল হয়। এটি প্রায়শই অক্ষর দ্বারা অক্ষর বা শব্দ দ্বারা শব্দ পার্সিংয়ের জন্য দরকারী।


37

কম্পিউটার বিজ্ঞানের প্রতিটি নিয়ম ভাঙার জন্য আমাকে ক্ষমা করুন, তবে একটি রাষ্ট্রীয় মেশিন হ'ল কয়েকটি (আমি কেবলমাত্র দুটি হাতই গণনা করতে পারি) এমন জায়গাগুলির মধ্যে যেখানে একটি gotoবিবৃতি কেবল আরও কার্যকর নয়, তবে আপনার কোডটিকে আরও পরিষ্কার এবং আরও সহজ করে তোলে। কারণ gotoবিবৃতি লেবেল এর উপর ভিত্তি করে, আপনি যদি এর পরিবর্তে সংখ্যার একটি জগাখিচুড়ি ট্র্যাক রাখতে অথবা একটি enum ব্যবহার করতে থাকার আপনার রাজ্যের নাম দিতে পারেন। এটি অনেক ক্লিনার কোডও তৈরি করে যেহেতু আপনাকে ফাংশন পয়েন্টার বা অতিরিক্ত স্যুইচ স্টেটমেন্টের সমস্ত অতিরিক্ত ক্রাফ্টের প্রয়োজন হয় না এবং লুপগুলি করার সময়। আমি কি এটি আরও দক্ষ বলে উল্লেখ করেছি?

একটি রাষ্ট্রের মেশিনটি দেখতে কেমন হতে পারে তা এখানে:

void state_machine() {
first_state:
    // Do some stuff here
    switch(some_var) {
    case 0:
        goto first_state;
    case 1:
        goto second_state;
    default:
        return;
    }

second_state:
    // Do some stuff here
    switch(some_var) {
    case 0:
        goto first_state;
    case 1:
        goto second_state;
    default:
        return;
    }
}

আপনি সাধারণ ধারণা পেতে। মুল বক্তব্যটি হ'ল আপনি একটি কার্যকর উপায়ে রাজ্য মেশিনটি প্রয়োগ করতে পারেন এবং পড়তে তুলনামূলকভাবে সহজ এবং এটি একটি রাষ্ট্রীয় মেশিনের দিকে তাকিয়ে আছে এমনটি পাঠকের কাছে চিৎকার করে। মনে রাখবেন যে আপনি যদি gotoবিবৃতি ব্যবহার করছেন তবে আপনাকে অবশ্যই সতর্ক থাকতে হবে কারণ এটি করার সময় নিজেকে পায়ে গুলি করা খুব সহজ shoot


4
এটি কেবল তখনই কাজ করে যদি রাষ্ট্রের মেশিনটি শীর্ষ-স্তরের অবজেক্টে থাকে। যে কোনও মুহুর্তে মাঝে মধ্যে পোল করা / বার্তাগুলি প্রেরণ করা হয়, সেই মুহুর্তের অবস্থা হওয়া দরকার, আপনি এই পদ্ধতির সাথে আটকে
রয়েছেন

1
এটি সত্যিকারের ক্ষেত্রে সহজতম ব্যতীত সমস্ত ক্ষেত্রে প্রাকৃতিক মাল্টিটাস্কিং ব্যবহার করতে বাধ্য করে।
ক্রেগ ম্যাককুইন

1
এই গোটোগুলি ফাংশন কলগুলির মাধ্যমে প্রতিস্থাপন করা যেতে পারে। এবং যদি কোনও প্রোফাইলার আপনাকে বলে যে ফাংশন কল ওভারহেডের কারণে আপনার প্রোগ্রামটি ডুবে যাচ্ছে, তবে আপনি প্রয়োজন অনুসারে কলগুলি প্রতিস্থাপন করতে পারেন।
আবতিন ফোরউজান্দেহ

7
@ অ্যাবটিনফোর্স ওজান্দেহ কেবল গ্লোসকে ফাংশন কলগুলির সাথে প্রতিস্থাপন করলে স্ট্যাকওভারফ্লো হবে কারণ কল স্ট্যাকটি কেবল কোনও ত্রুটির ক্ষেত্রে সাফ হয়ে গেলে।
জাস্টম্যাক্সিমিয়ামম পাওয়ার

আমি গোটো পদ্ধতির সাথে একমত এখানে ম্যাক্রোগুলির একটি সেট রয়েছে যা এটি চিত্রিত করে। এবং ম্যাক্রোগুলি আপনার কোডটিকে এমনভাবে কাঠামোযুক্ত করে তোলে যেন আপনি এটিকে সাধারণত আপনার মতো কোড করে রেখেছিলেন। এটি বাধাপ্রাপ্ত স্তরেও কাজ করে যা সাধারণত যেখানে রাষ্ট্রীয় মেশিনগুলির কোডেপ্রজেক্ট
আর্টিকেল

30

আপনি স্টেট মেশিন সংকলক http://smc.sourceforge.net/ বিবেচনা করতে পারেন

এই দুর্দান্ত ওপেন সোর্স ইউটিলিটিটি একটি সরল ভাষায় একটি রাষ্ট্রের মেশিনের বিবরণ গ্রহণ করে এবং এটি সি এবং সি ++ সহ এক ডজন বা আরও যে কোনও একটি ভাষার সংকলন করে। ইউটিলিটি নিজেই জাভাতে লেখা এবং এটি একটি বিল্ডের অংশ হিসাবে অন্তর্ভুক্ত করা যেতে পারে।

এটি করার কারণ, জিওএফ স্টেট প্যাটার্ন বা অন্য কোনও পদ্ধতির ব্যবহার করে হ্যান্ড কোডিংয়ের পরিবর্তে হ'ল একবার যদি আপনার রাজ্য মেশিন কোড হিসাবে প্রকাশিত হয় তবে অন্তর্নিহিত কাঠামোটি বয়লারপ্লেটের ওজনের নিচে অদৃশ্য হয়ে যায় যা সমর্থন করার জন্য উত্পন্ন করা দরকার। এই পদ্ধতির ব্যবহার আপনাকে উদ্বেগের একটি দুর্দান্ত বিভাজন দেয় এবং আপনি আপনার রাষ্ট্র মেশিনের কাঠামোকে 'দৃশ্যমান' রাখেন। স্বয়ংক্রিয়ভাবে উত্পন্ন কোডটি এমন মডিউলগুলিতে চলে যায় যেগুলি আপনাকে স্পর্শ করার দরকার নেই, যাতে আপনি লিখেছেন এমন সমর্থনকারী কোডটি প্রভাবিত না করেই আপনি রাজ্য মেশিনের কাঠামোর সাথে পিছনে ফিরে যেতে পারেন।

দুঃখিত, আমি অত্যধিক উত্সাহী হয়ে যাচ্ছি এবং নিঃসন্দেহে সবাইকে বিদায় দিচ্ছি। তবে এটি একটি শীর্ষ খাঁটি ইউটিলিটি এবং ভাল-ডকুমেন্টেডও।


20

মিরো সামেকের কাজ (ব্লগ রাজ্য স্পেস , ওয়েবসাইট রাজ্য মেশিন ও সরঞ্জামসমূহ ) যাচাই করতে ভুলবেন না , যার সি / সি ++ ব্যবহারকারী জার্নালে নিবন্ধগুলি দুর্দান্ত ছিল।

ওয়েবসাইট উভয় ওপেন সোর্স এবং একটি বাণিজ্যিক লাইসেন্স একটি সম্পূর্ণ হয়েছে (সি / সি ++) বাস্তবায়ন রয়েছে রাষ্ট্র মেশিন ফ্রেমওয়ার্ক (QP কাঠামো) , একটি ইভেন্ট হ্যান্ডলার (QEP) , একটি মৌলিক মডেলিং সরঞ্জাম (QM) এবং ট্রেসিং সরঞ্জাম (QSpy) যা রাষ্ট্রীয় মেশিনগুলি আঁকতে, কোড তৈরি করতে এবং তাদের ডিবাগ করার অনুমতি দিন।

বইটি কীভাবে / কেন প্রয়োগ করা যায় এবং কীভাবে এটি ব্যবহার করতে হয় সে সম্পর্কে একটি বিস্তৃত ব্যাখ্যা রয়েছে এবং এটি হায়ারিকাল এবং সসীম রাষ্ট্র মেশিনগুলির মৌলিক বিষয়গুলি বোঝার জন্য দুর্দান্ত উপাদান।

এম্বেড থাকা প্ল্যাটফর্মগুলি সহ সফ্টওয়্যারটি ব্যবহারের জন্য ওয়েবসাইটটিতে বেশ কয়েকটি বোর্ড সমর্থন প্যাকেজগুলির লিঙ্ক রয়েছে।


আমি আপনার শ্লেষ অনুসরণ করে প্রশ্নের শিরোনাম পরিবর্তন করেছি।
jldupont

@ জাল্ডুপন্ট: আমি কেবল বুঝিয়েছি যে স্পষ্ট করে দেওয়া আরও ভাল। আমি এখন আমার উত্তরের অপ্রাসঙ্গিক অংশগুলি মুছে ফেলেছি।
ড্যানিয়েল দারানাস

1
সফলভাবে নিজেই সফ্টওয়্যারটি ব্যবহার করে আমি ওয়েবসাইট / বইয়ের উপর কী আশা করব তা যুক্ত করেছি; এটি আমার বইয়ের তাকের সেরা বই।
অ্যাডরিয়ান

@ অ্যাড্রিয়েন, দুর্দান্ত ব্যাখ্যা! আমি কেবল ওয়েবসাইটের হোমটি পরিবর্তন করেছি, আগের লিঙ্কটি কাজ করা বন্ধ করে দিয়েছে।
ড্যানিয়েল দারানাস

2
লিঙ্কগুলি হয় মারা গেছে বা এম্বেডড সফ্টওয়্যারটিতে তার দিক পরিবর্তন করেছে বলে মনে হয় সাইটের হোমপৃষ্ঠায় প্রদর্শিত হয়। আপনি এখনও state-machine.com/resources/articles.php তে কিছু সামগ্রী দেখতে পাচ্ছেন , তবে সেখানেও বেশিরভাগ রাষ্ট্র মেশিন সম্পর্কিত লিঙ্কগুলি মারা গেছে। এটি সেখানে একমাত্র ভাল লিঙ্কগুলির মধ্যে একটি: state-machine.com/resources/…
টাটিয়ানা রাচেভা

11

আমি প্যাক্সিডিয়াবলোর বর্ণনার সাথে অনুরূপ কিছু করেছি, কেবলমাত্র রাষ্ট্র / ইভেন্ট ট্রানজিশনের একটি অ্যারের পরিবর্তে, আমি ফাংশন পয়েন্টারগুলির একটি 2-মাত্রিক অ্যারে সেট করেছিলাম, একটি অক্ষের সূচক হিসাবে ইভেন্টের মান এবং বর্তমান রাষ্ট্রীয় মান হিসাবে অন্যটি. তারপরে আমি কেবল কল করি state = state_table[event][state](params)এবং সঠিক জিনিসটি ঘটে। অবৈধ অবস্থা / ইভেন্ট সংমিশ্রণগুলিকে উপস্থাপন করা ঘরগুলি অবশ্যই কোনও ফাংশনটির জন্য একটি পয়েন্টার পায় অবশ্যই।

স্পষ্টতই, এটি কেবল তখনই কার্যকর হয় যদি রাষ্ট্র এবং ইভেন্টের মান উভয়ই সামঞ্জস্যপূর্ণ হয় এবং 0 থেকে শুরু হয় বা পর্যাপ্তভাবে বন্ধ হয়।


1
এই সমাধানটি সুন্দরভাবে স্কেল করে না এমন মনে হয়: খুব বেশি টেবিল ভর্তি, না?
jldupont

2
+1 টি। এখানে স্কেলিংয়ের সমস্যাটি মেমোরি - আমার নিজের সমাধানটিতে একটি স্কেলিং ইস্যু পুনরায় সময় হয়, অর্থাত্ ট্রানজিশন টেবিলটি স্ক্যান করতে সময় নেওয়া হয় (যদিও আপনি সর্বাধিক সাধারণ রূপান্তরগুলির জন্য ম্যানুয়ালি অপ্টিমাইজ করতে পারেন)। এই গতির জন্য স্মৃতি ত্যাগ করে - এটি কেবল একটি বাণিজ্য- আপনার সম্ভবত সীমার জন্য চেকের দরকার আছে তবে এটি কোনও খারাপ সমাধান নয়।
প্যাক্সিডিয়াবল

বলছি - আমার মন্তব্যটি উদ্দেশ্য হিসাবে প্রকাশিত হয়নি: আমার অর্থ এটি অনেক বেশি শ্রমসাধ্য এবং ত্রুটির প্রবণ। আপনি যদি কোনও রাজ্য / ইভেন্ট যোগ করেন তবে প্রচুর সম্পাদনা করা দরকার।
jldupont

3
কেউ বলেনি 2D অ্যারে হাতে হাতে আরম্ভ করা হয়েছিল। হতে পারে এমন কিছু আছে যা একটি কনফিগারেশন ফাইল পড়ে এবং এটি তৈরি করে (বা কমপক্ষে অবশ্যই থাকতে পারে)।
জন জুইনক

এর মতো অ্যারেগুলি আরম্ভ করার একটি উপায় হ'ল প্রিপ্রোসেসরটি দেরীতে বাইন্ডার হিসাবে ব্যবহার করা (তাড়াতাড়ি বাইন্ডিংয়ের বিপরীতে)। আপনি সমস্ত রাজ্যের একটি তালিকা সংজ্ঞায়িত করেছেন #define STATE_LIST() \STATE_LIST_ENTRY(state1)\STATE_LIST_ENTRY(state2)\...(প্রত্যেকের পরে একটি অন্তর্নিহিত নিউলাইন \ ) যেখানে আপনি (পুনরায়) যখন আপনি STATE_LIST ম্যাক্রো ব্যবহার করেন তখন প্রবেশ ম্যাক্রো সংজ্ঞায়িত করেন। উদাহরণ - রাজ্য নামের একটি অ্যারের উপার্জন: #define STATE_LIST_ENTRY(s) #s , \n const char *state_names[] = { STATE_LIST() };\n #undef STATE_LIST_ENTRY। কিছু প্রথমে সেট আপ করার জন্য কাজ করে তবে এটি অত্যন্ত শক্তিশালী। নতুন রাজ্য যুক্ত করুন -> কোনও মিসের নিশ্চয়তা নেই।
hlovdal

9

স্টিফান হেইনজম্যান তার নিবন্ধে একটি খুব সুন্দর টেম্পলেট-ভিত্তিক সি ++ স্টেট মেশিন "কাঠামো" দিয়েছেন ।

নিবন্ধে একটি সম্পূর্ণ কোড ডাউনলোডের কোনও লিঙ্ক নেই বলে, আমি কোনও প্রকল্পে কোডটি পেস্ট করতে এবং এটি পরীক্ষা করে দেখার স্বাধীনতা নিয়েছি। নীচের স্টাফটি পরীক্ষা করা হয়েছে এবং এতে কয়েকটি ছোটখাট তবে সুস্পষ্টভাবে অনুপস্থিত টুকরো রয়েছে।

এখানে প্রধান উদ্ভাবন হ'ল সংকলকটি খুব দক্ষ কোড তৈরি করছে। খালি প্রবেশ / প্রস্থান ক্রিয়াকলাপগুলির কোনও মূল্য নেই। খালি-খালি প্রবেশ / প্রস্থান ক্রিয়াকলাপগুলি অন্তর্ভুক্ত। সংকলকও রাজ্যচর্টের সম্পূর্ণতা যাচাই করছে। অনুপস্থিত ক্রিয়াগুলি লিঙ্কিং ত্রুটিগুলি উত্পন্ন করে। ধরা পড়ে না এমন একমাত্র জিনিসটি হচ্ছে অনুপস্থিত Top::init

এটি মিরো সামেকের বাস্তবায়নের খুব সুন্দর বিকল্প, যদি আপনি কী হারিয়েছেন তা ছাড়া বাঁচতে পারেন - এটি সম্পূর্ণ ইউএমএল স্টেটচার্ট বাস্তবায়ন থেকে অনেক দূরে, যদিও এটি ইউএমএল শব্দার্থকে সঠিকভাবে প্রয়োগ করে, অন্যদিকে ডিজাইন দ্বারা সামেকের কোড প্রস্থান / উত্তরণ পরিচালনা করে না / সঠিক ক্রমে প্রবেশের ক্রিয়া।

এই কোডটি আপনার যা করা দরকার সেটির জন্য যদি কাজ করে এবং আপনার সিস্টেমের জন্য আপনার একটি শালীন সি ++ সংকলক রয়েছে, তবে এটি সম্ভবত মিরোর সি / সি ++ বাস্তবায়নের চেয়ে আরও ভাল সম্পাদন করবে। সংকলকটি আপনার জন্য একটি সমতল, ও (1) রূপান্তর রাষ্ট্র মেশিন বাস্তবায়ন উত্পন্ন করে। যদি সমাবেশের আউটপুট নিরীক্ষণ নিশ্চিত করে যে অনুকূলিতকরণগুলি পছন্দসইভাবে কাজ করে তবে আপনি তাত্ত্বিক পারফরম্যান্সের কাছাকাছি চলে যান। সেরা অংশ: এটি তুলনামূলকভাবে ক্ষুদ্র, কোড বোঝা সহজ।

#ifndef HSM_HPP
#define HSM_HPP

// This code is from:
// Yet Another Hierarchical State Machine
// by Stefan Heinzmann
// Overload issue 64 december 2004
// http://accu.org/index.php/journals/252

/* This is a basic implementation of UML Statecharts.
 * The key observation is that the machine can only
 * be in a leaf state at any given time. The composite
 * states are only traversed, never final.
 * Only the leaf states are ever instantiated. The composite
 * states are only mechanisms used to generate code. They are
 * never instantiated.
 */

// Helpers

// A gadget from Herb Sutter's GotW #71 -- depends on SFINAE
template<class D, class B>
class IsDerivedFrom {
    class Yes { char a[1]; };
    class No  { char a[10]; };
    static Yes Test(B*); // undefined
    static No Test(...); // undefined
public:
    enum { Res = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) ? 1 : 0 };
};

template<bool> class Bool {};

// Top State, Composite State and Leaf State

template <typename H>
struct TopState {
    typedef H Host;
    typedef void Base;
    virtual void handler(Host&) const = 0;
    virtual unsigned getId() const = 0;
};

template <typename H, unsigned id, typename B>
struct CompState;

template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > >
struct CompState : B {
    typedef B Base;
    typedef CompState<H, id, Base> This;
    template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); }
    static void init(H&); // no implementation
    static void entry(H&) {}
    static void exit(H&) {}
};

template <typename H>
struct CompState<H, 0, TopState<H> > : TopState<H> {
    typedef TopState<H> Base;
    typedef CompState<H, 0, Base> This;
    template <typename X> void handle(H&, const X&) const {}
    static void init(H&); // no implementation
    static void entry(H&) {}
    static void exit(H&) {}
};

template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > >
struct LeafState : B {
    typedef H Host;
    typedef B Base;
    typedef LeafState<H, id, Base> This;
    template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); }
    virtual void handler(H& h) const { handle(h, *this); }
    virtual unsigned getId() const { return id; }
    static void init(H& h) { h.next(obj); } // don't specialize this
    static void entry(H&) {}
    static void exit(H&) {}
    static const LeafState obj; // only the leaf states have instances
};

template <typename H, unsigned id, typename B>
const LeafState<H, id, B> LeafState<H, id, B>::obj;

// Transition Object

template <typename C, typename S, typename T>
// Current, Source, Target
struct Tran {
    typedef typename C::Host Host;
    typedef typename C::Base CurrentBase;
    typedef typename S::Base SourceBase;
    typedef typename T::Base TargetBase;
    enum { // work out when to terminate template recursion
        eTB_CB = IsDerivedFrom<TargetBase, CurrentBase>::Res,
        eS_CB = IsDerivedFrom<S, CurrentBase>::Res,
        eS_C = IsDerivedFrom<S, C>::Res,
        eC_S = IsDerivedFrom<C, S>::Res,
        exitStop = eTB_CB && eS_C,
        entryStop = eS_C || eS_CB && !eC_S
    };
    // We use overloading to stop recursion.
    // The more natural template specialization
    // method would require to specialize the inner
    // template without specializing the outer one,
    // which is forbidden.
    static void exitActions(Host&, Bool<true>) {}
    static void exitActions(Host&h, Bool<false>) {
        C::exit(h);
        Tran<CurrentBase, S, T>::exitActions(h, Bool<exitStop>());
    }
    static void entryActions(Host&, Bool<true>) {}
    static void entryActions(Host& h, Bool<false>) {
        Tran<CurrentBase, S, T>::entryActions(h, Bool<entryStop>());
        C::entry(h);
    }
    Tran(Host & h) : host_(h) {
        exitActions(host_, Bool<false>());
    }
    ~Tran() {
        Tran<T, S, T>::entryActions(host_, Bool<false>());
        T::init(host_);
    }
    Host& host_;
};

// Initializer for Compound States

template <typename T>
struct Init {
    typedef typename T::Host Host;
    Init(Host& h) : host_(h) {}
    ~Init() {
        T::entry(host_);
        T::init(host_);
    }
    Host& host_;
};

#endif // HSM_HPP

পরীক্ষার কোড অনুসরণ করা হয়।

#include <cstdio>
#include "hsm.hpp"
#include "hsmtest.hpp"

/* Implements the following state machine from Miro Samek's
 * Practical Statecharts in C/C++
 *
 * |-init-----------------------------------------------------|
 * |                           s0                             |
 * |----------------------------------------------------------|
 * |                                                          |
 * |    |-init-----------|        |-------------------------| |
 * |    |       s1       |---c--->|            s2           | |
 * |    |----------------|<--c----|-------------------------| |
 * |    |                |        |                         | |
 * |<-d-| |-init-------| |        | |-init----------------| | |
 * |    | |     s11    |<----f----| |          s21        | | |
 * | /--| |------------| |        | |---------------------| | |
 * | a  | |            | |        | |                     | | |
 * | \->| |            |------g--------->|-init------|    | | |
 * |    | |____________| |        | |-b->|    s211   |---g--->|
 * |    |----b---^       |------f------->|           |    | | |
 * |    |________________|        | |<-d-|___________|<--e----|
 * |                              | |_____________________| | |
 * |                              |_________________________| |
 * |__________________________________________________________|
 */

class TestHSM;

typedef CompState<TestHSM,0>     Top;
typedef CompState<TestHSM,1,Top>   S0;
typedef CompState<TestHSM,2,S0>      S1;
typedef LeafState<TestHSM,3,S1>        S11;
typedef CompState<TestHSM,4,S0>      S2;
typedef CompState<TestHSM,5,S2>        S21;
typedef LeafState<TestHSM,6,S21>         S211;

enum Signal { A_SIG, B_SIG, C_SIG, D_SIG, E_SIG, F_SIG, G_SIG, H_SIG };

class TestHSM {
public:
    TestHSM() { Top::init(*this); }
    ~TestHSM() {}
    void next(const TopState<TestHSM>& state) {
        state_ = &state;
    }
    Signal getSig() const { return sig_; }
    void dispatch(Signal sig) {
        sig_ = sig;
        state_->handler(*this);
    }
    void foo(int i) {
        foo_ = i;
    }
    int foo() const {
        return foo_;
    }
private:
    const TopState<TestHSM>* state_;
    Signal sig_;
    int foo_;
};

bool testDispatch(char c) {
    static TestHSM test;
    if (c<'a' || 'h'<c) {
        return false;
    }
    printf("Signal<-%c", c);
    test.dispatch((Signal)(c-'a'));
    printf("\n");
    return true;
}

int main(int, char**) {
    testDispatch('a');
    testDispatch('e');
    testDispatch('e');
    testDispatch('a');
    testDispatch('h');
    testDispatch('h');
    return 0;
}

#define HSMHANDLER(State) \
    template<> template<typename X> inline void State::handle(TestHSM& h, const X& x) const

HSMHANDLER(S0) {
    switch (h.getSig()) {
    case E_SIG: { Tran<X, This, S211> t(h);
        printf("s0-E;");
        return; }
    default:
        break;
    }
    return Base::handle(h, x);
}

HSMHANDLER(S1) {
    switch (h.getSig()) {
    case A_SIG: { Tran<X, This, S1> t(h);
        printf("s1-A;"); return; }
    case B_SIG: { Tran<X, This, S11> t(h);
        printf("s1-B;"); return; }
    case C_SIG: { Tran<X, This, S2> t(h);
        printf("s1-C;"); return; }
    case D_SIG: { Tran<X, This, S0> t(h);
        printf("s1-D;"); return; }
    case F_SIG: { Tran<X, This, S211> t(h);
        printf("s1-F;"); return; }
    default: break;
    }
    return Base::handle(h, x);
}

HSMHANDLER(S11) {
    switch (h.getSig()) {
    case G_SIG: { Tran<X, This, S211> t(h);
        printf("s11-G;"); return; }
    case H_SIG: if (h.foo()) {
            printf("s11-H");
            h.foo(0); return;
        } break;
    default: break;
    }
    return Base::handle(h, x);
}

HSMHANDLER(S2) {
    switch (h.getSig()) {
    case C_SIG: { Tran<X, This, S1> t(h);
        printf("s2-C"); return; }
    case F_SIG: { Tran<X, This, S11> t(h);
        printf("s2-F"); return; }
    default: break;
    }
    return Base::handle(h, x);
}

HSMHANDLER(S21) {
    switch (h.getSig()) {
    case B_SIG: { Tran<X, This, S211> t(h);
        printf("s21-B;"); return; }
    case H_SIG: if (!h.foo()) {
            Tran<X, This, S21> t(h);
            printf("s21-H;"); h.foo(1);
            return;
        } break;
    default: break;
    }
    return Base::handle(h, x);
}

HSMHANDLER(S211) {
    switch (h.getSig()) {
    case D_SIG: { Tran<X, This, S21> t(h);
        printf("s211-D;"); return; }
    case G_SIG: { Tran<X, This, S0> t(h);
        printf("s211-G;"); return; }
    }
    return Base::handle(h, x);
}

#define HSMENTRY(State) \
    template<> inline void State::entry(TestHSM&) { \
        printf(#State "-ENTRY;"); \
    }

HSMENTRY(S0)
HSMENTRY(S1)
HSMENTRY(S11)
HSMENTRY(S2)
HSMENTRY(S21)
HSMENTRY(S211)

#define HSMEXIT(State) \
    template<> inline void State::exit(TestHSM&) { \
        printf(#State "-EXIT;"); \
    }

HSMEXIT(S0)
HSMEXIT(S1)
HSMEXIT(S11)
HSMEXIT(S2)
HSMEXIT(S21)
HSMEXIT(S211)

#define HSMINIT(State, InitState) \
    template<> inline void State::init(TestHSM& h) { \
       Init<InitState> i(h); \
       printf(#State "-INIT;"); \
    }

HSMINIT(Top, S0)
HSMINIT(S0, S1)
HSMINIT(S1, S11)
HSMINIT(S2, S21)
HSMINIT(S21, S211)

হুম ... স্টেম আপনার কোডটিতে অনুপস্থিত। প্রথমে আপনি দুটি শিরোলেখ অন্তর্ভুক্ত করুন তবে কেবল প্রথমটি সরবরাহ করুন। আমি যখন কেবল "অন্তর্ভুক্ত" বিবৃতিটি মন্তব্য করি তখন সংকলন করার সময় আমি এই ত্রুটিটি পেয়েছি: d: \ 1 \ hsm> g ++ test.cpp test.cpp: 195: 1: ত্রুটি: 'স্ট্যাটিক শূন্যপদ <<, আইডি, বি> এর বিশেষীকরণ :: init (এইচ এবং) [এইচ = টেস্টএইচএসএম সহ; স্বাক্ষরবিহীন int id = 0u; বি = কম্পিস্টেট <টেস্টএইচএসএম, 0 এউ, টপস্টেট <টেস্টএইচএসএম>>] 'ইনস্ট্যান্টেশন পরে
ফ্রেডি চপিন

আমাকে টেস্টএইচএসএম শ্রেণির উপরের হতে সমস্ত এইচএসএমআইএনটি () এর সংজ্ঞা স্থানান্তর করতে হয়েছিল এবং এটি সংকলন করে এবং কাজ করে (; কেবলমাত্র ভুলটি হ'ল সত্য যে সমস্ত রূপান্তরগুলি "বাহ্যিক", যখন তাদের "অভ্যন্তরীণ" হওয়া উচিত - সেখানে ছিল নিবন্ধে এটি সম্পর্কে কিছু বিতর্ক এবং লেখক সিদ্ধান্ত নিয়েছিলেন যে "এক্সট্রেনাল" সঠিক ছিল, তবে তীরগুলি "অভ্যন্তরীণ" হিসাবে ব্যবহার করার পরামর্শ দেয়
ফ্রেডি চপিন

5

আমি রাষ্ট্রীয় মেশিনগুলির জন্য যে কৌশলটি পছন্দ করি তা হ'ল (প্রোগ্রাম নিয়ন্ত্রণের জন্য কমপক্ষে একটি) ফাংশন পয়েন্টার ব্যবহার করা। প্রতিটি রাষ্ট্র একটি পৃথক ফাংশন দ্বারা প্রতিনিধিত্ব করা হয়। ফাংশনটি একটি ইনপুট প্রতীক নেয় এবং পরবর্তী অবস্থার জন্য ফাংশন পয়েন্টারটি দেয়। কেন্দ্রীয় প্রেরণকারী লুপ মনিটরগুলি পরবর্তী ইনপুট নেয়, এটিকে বর্তমান অবস্থায় খাওয়ায় এবং ফলাফলটি প্রক্রিয়া করে।

এতে টাইপিং কিছুটা অদ্ভুত হয়ে যায়, যেহেতু সি এর কাছে ফাংশন পয়েন্টারগুলি নিজেই ফিরে আসার নির্দেশ করার উপায় নেই, সুতরাং রাষ্ট্রীয় কার্যগুলি ফিরে আসে void*। তবে আপনি এটির মতো কিছু করতে পারেন:

typedef void* (*state_handler)(input_symbol_t);
void dispatch_fsm()
{
    state_handler current = initial_handler;
    /* Let's assume returning null indicates end-of-machine */
    while (current) {
        current = current(get_input);
    }
 }

তারপরে আপনার স্বতন্ত্র রাষ্ট্রীয় ক্রিয়াকলাপগুলি তাদের ইনপুটটি প্রসেস করতে এবং উপযুক্ত মানটি ফিরিয়ে দিতে পারে।


+1 যা সত্যিই দুর্দান্ত, এবং রূপান্তর কার্যের ভিতরে কার্যকারিতা হস্তান্তর করার জন্য সুন্দর জায়গা সরবরাহ করে
ফায়ার ক্র

5

সিম্পল কেস

enum event_type { ET_THIS, ET_THAT };
union event_parm { uint8_t this; uint16_t that; }
static void handle_event(enum event_type event, union event_parm parm)
{
  static enum { THIS, THAT } state;
  switch (state)
  {
    case THIS:
    switch (event)
    {
      case ET_THIS:
      // Handle event.
      break;

      default:
      // Unhandled events in this state.
      break;
    }
    break;

    case THAT:
    // Handle state.
    break;
  }
}

পয়েন্টগুলি: রাজ্যটি শুধুমাত্র সংকলনের ইউনিটেই নয়, ইভেন্ট_হ্যান্ডলারটির কাছেও ব্যক্তিগত। প্রয়োজনীয় বিবেচিত যা কিছু নির্মাণ প্রয়োজন তা ব্যবহার করে প্রধান মামলাগুলি আলাদা করে পরিচালনা করা যেতে পারে।

আরও জটিল কেস

যখন স্যুইচ বেশ কয়েকটি পর্দার চেয়েও বেশি পূর্ণ হয়ে যায়, তখন প্রতিটি ফাংশনকে সরাসরি পরিচালনা করার জন্য একটি স্টেট টেবিল ব্যবহার করে এটি প্রতিটি ফাংশনগুলিতে ভাগ করুন। রাজ্যটি এখনও ইভেন্ট হ্যান্ডলারের ব্যক্তিগত। রাষ্ট্র পরিচালনার কাজগুলি পরের রাজ্যে ফিরে আসে। প্রয়োজনে কিছু ইভেন্ট এখনও মূল ইভেন্ট হ্যান্ডলারটিতে বিশেষ চিকিত্সা পেতে পারে। আমি রাষ্ট্র প্রবেশ এবং প্রস্থান এবং সম্ভবত রাষ্ট্রের মেশিন শুরুর জন্য সিউডো-ইভেন্টগুলি ছুঁড়ে দিতে চাই:

enum state_type { THIS, THAT, FOO, NA };
enum event_type { ET_START, ET_ENTER, ET_EXIT, ET_THIS, ET_THAT, ET_WHATEVER, ET_TIMEOUT };
union event_parm { uint8_t this; uint16_t that; };
static void handle_event(enum event_type event, union event_parm parm)
{
  static enum state_type state;
  static void (* const state_handler[])(enum event_type event, union event_parm parm) = { handle_this, handle_that };
  enum state_type next_state = state_handler[state](event, parm);
  if (NA != next_state && state != next_state)
  {
    (void)state_handler[state](ET_EXIT, 0);
    state = next_state;
    (void)state_handler[state](ET_ENTER, 0);
  }
}

আমি নিশ্চিত নই যে আমি সিনট্যাক্সটি পেরেক দিয়েছি, বিশেষত ফাংশন পয়েন্টারগুলির অ্যারে সম্পর্কিত। একটি সংকলকের মাধ্যমে আমি এর কোনওটি চালাইনি। পর্যালোচনা করার পরে, আমি লক্ষ্য করেছি যে সিউডো ইভেন্টগুলি পরিচালনা করার সময় আমি স্পষ্টভাবে পরবর্তী অবস্থাটি বাতিল করতে ভুলে গিয়েছি (স্টেট_হ্যান্ডলার ()) এ কল করার আগে (অকার্যকর) প্রথম বন্ধনী। এটি এমন কিছু যা আমি করতে পছন্দ করি যদিও সংকলকরা নিঃশব্দে বাদ দেওয়া গ্রহণ করে। এটি কোডটির পাঠকদের জানিয়েছে যে "হ্যাঁ, আমি রিটার্ন মানটি ব্যবহার না করেই ফাংশনটি কল করতে চাইছিলাম", এবং এটি স্থিতিশীল বিশ্লেষণ সরঞ্জামগুলি সম্পর্কে সতর্কতা থেকে বিরত থাকতে পারে। এটি মূর্খতাযুক্ত হতে পারে কারণ আমি অন্য কারও সাথে এটি করতে দেখিনি।

পয়েন্টস: জটিলতার একটি সামান্য বিট যোগ করা (পরবর্তী রাষ্ট্রটি বর্তমানের চেয়ে পৃথক কিনা তা পরীক্ষা করে), অন্য কোথাও নকল কোড এড়াতে পারে, কারণ রাষ্ট্র পরিচালনার কাজগুলি সিউডোর ঘটনাগুলি উপভোগ করতে পারে যা একটি রাজ্য প্রবেশ করানো এবং বামে প্রদর্শিত হয়। মনে রাখবেন সিউডো ইভেন্টগুলি পরিচালনা করার সময় রাষ্ট্র পরিবর্তন করতে পারে না, কারণ এই ইভেন্টগুলির পরে রাষ্ট্র পরিচালকের ফলাফল বাতিল করা হয়। আপনি অবশ্যই আচরণটি পরিবর্তন করতে বেছে নিতে পারেন।

একটি রাষ্ট্রের হ্যান্ডলার এর মতো দেখতে পাবেন:

static enum state_type handle_this(enum event_type event, union event_parm parm)
{
  enum state_type next_state = NA;
  switch (event)
  {
    case ET_ENTER:
    // Start a timer to do whatever.
    // Do other stuff necessary when entering this state.
    break;

    case ET_WHATEVER:
    // Switch state.
    next_state = THAT;
    break;

    case ET_TIMEOUT:
    // Switch state.
    next_state = FOO;
    break;

    case ET_EXIT:
    // Stop the timer.
    // Generally clean up this state.
    break;
  }
  return next_state;
}

আরও জটিলতা

সংকলন ইউনিট যখন খুব বড় হয়ে যায় (আপনার যা মনে হয় তা যাই হোক না কেন, আমার প্রায় 1000 টি লাইন বলা উচিত), প্রতিটি রাষ্ট্রের হ্যান্ডলারকে একটি পৃথক ফাইলে রাখুন। যখন প্রতিটি রাষ্ট্রের হ্যান্ডলার কয়েকটি পর্দার চেয়ে দীর্ঘ হয়, তখন প্রতিটি ইভেন্টকে আলাদা ফাংশনে বিভক্ত করুন, রাষ্ট্রের স্যুইচটি যেভাবে বিভক্ত হয়েছিল তার অনুরূপ। আপনি রাজ্য থেকে আলাদাভাবে বা একটি সাধারণ টেবিল ব্যবহার করে বা বিভিন্ন স্কিমের সমন্বয় করে বিভিন্ন উপায়ে এটি করতে পারেন। তাদের কিছু এখানে অন্যদের দ্বারা কভার করা হয়েছে। আপনার টেবিলগুলি বাছাই করুন এবং গতি প্রয়োজন হলে বাইনারি অনুসন্ধান ব্যবহার করুন।

জেনেরিক প্রোগ্রামিং

টেবিলে বাছাই করা এমনকি বিবরণ থেকে স্টেট মেশিন তৈরি করা, আপনাকে "প্রোগ্রাম সম্পর্কিত প্রোগ্রাম লেখার" অনুমতি দেওয়ার মতো বিষয়গুলির সাথে মোকাবিলা করার জন্য আমার প্রিপ্রোসেসরটি পছন্দ করা উচিত। আমি বিশ্বাস করি এটিই বুস্ট লোকেরা সি ++ টেম্পলেটগুলির জন্য ব্যবহার করছে তবে আমি সিনট্যাক্স ক্রিপ্টিকটি পাই।

দ্বিমাত্রিক টেবিল

আমি অতীতে রাজ্য / ইভেন্ট সারণী ব্যবহার করেছি তবে আমাকে বলতে হবে যে সহজতম ক্ষেত্রে আমি এগুলি প্রয়োজনীয় মনে করি না এবং আমি স্যুইচ বিবৃতিটির স্পষ্টতা এবং পঠনযোগ্যতাটিকে পছন্দ করি এমনকি এটি যদি একটি পর্দা পূর্ণতা নাও দেয়। আরও জটিল ক্ষেত্রে টেবিলগুলি দ্রুত অন্যদের হাত থেকে বেরিয়ে যায় যেমন অন্যরা লিখেছেন। আমি এখানে উপস্থাপিত প্রতিমাগুলি আপনাকে মেমরি গ্রাসকারী টেবিলটি বজায় না রেখেই (যদিও এটি প্রোগ্রামের স্মৃতি হতে পারে) মজাদার টেবিল বজায় না রেখে আপনাকে বেশ কয়েকটি ইভেন্ট যোগ করার অনুমতি দেয়।

দাবি পরিত্যাগী

বিশেষ প্রয়োজনগুলি এই আইডিয়ামগুলিকে কম দরকারী হিসাবে রেন্ডার করতে পারে তবে আমি সেগুলি খুব স্পষ্ট এবং রক্ষণাবেক্ষণের মতো পেয়েছি।


আমি কেবলমাত্র সমিতির জন্য একটি পরিবর্তনশীল নাম বা প্রতীক হিসাবে 'এটি' এড়াতে পারি, এমনকি এটি আসলে কোনও সংরক্ষিত শব্দ না হলেও।
এক্সটিএল

4

চূড়ান্তভাবে অনিরীক্ষিত, তবে কোডটি মজাদার, এখন আমার মূল উত্তরের চেয়ে আরও পরিশ্রুত সংস্করণে; আপ-টু-ডেট সংস্করণগুলি মার্উরিয়াল.আইন্টাকুইশন.অর্গ.এ পাওয়া যাবে :

sm.h

#ifndef SM_ARGS
#error "SM_ARGS undefined: " \
    "use '#define SM_ARGS (void)' to get an empty argument list"
#endif

#ifndef SM_STATES
#error "SM_STATES undefined: " \
    "you must provide a list of comma-separated states"
#endif

typedef void (*sm_state) SM_ARGS;
static const sm_state SM_STATES;

#define sm_transit(STATE) ((sm_state (*) SM_ARGS)STATE)

#define sm_def(NAME) \
    static sm_state NAME ## _fn SM_ARGS; \
    static const sm_state NAME = (sm_state)NAME ## _fn; \
    static sm_state NAME ## _fn SM_ARGS

example.c

#include <stdio.h>

#define SM_ARGS (int i)
#define SM_STATES EVEN, ODD
#include "sm.h"

sm_def(EVEN)
{
    printf("even %i\n", i);
    return ODD;
}

sm_def(ODD)
{
    printf("odd  %i\n", i);
    return EVEN;
}

int main(void)
{
    int i = 0;
    sm_state state = EVEN;

    for(; i < 10; ++i)
        state = sm_transit(state)(i);

    return 0;
}

14
আমি "অত্যন্ত অরক্ষিত" মন্তব্যটি পছন্দ করি। দেখে মনে হচ্ছে যে সেখানে
অবিবাহিততার

@ ক্রিসটফ এই উত্তরের লিঙ্কটি নষ্ট হয়েছে। এছাড়াও, আপনি এই কোডটি পরীক্ষা করেছেন নাকি? যদি এটি পরীক্ষা করা হয় এবং কাজ করে তবে আপনার উত্তরটি থেকে তা সরানো উচিত। এছাড়াও সম্ভবত ম্যাক্রোগুলি প্রসারিত হওয়ার পরে এই কোডটির ফলাফল কী কোডের একটি উদাহরণ দেখান। আমি সাধারণ ধারণা পছন্দ করি।
জোয়াকিম

4

আমি প্যাক্সেবলের উত্তরটি সত্যিই পছন্দ করেছি এবং গার্ড ভেরিয়েবল এবং স্টেট মেশিনের নির্দিষ্ট ডেটার মতো আমার অ্যাপ্লিকেশনটির জন্য সমস্ত অনুপস্থিত বৈশিষ্ট্য প্রয়োগ করার সিদ্ধান্ত নিয়েছি।

সম্প্রদায়ের সাথে ভাগ করে নেওয়ার জন্য আমি আমার প্রয়োগটি এই সাইটে আপলোড করেছি। এটিআরএমের জন্য আইএআর এম্বেডড ওয়ার্কবেঞ্চ ব্যবহার করে এটি পরীক্ষা করা হয়েছে।

https://sourceforge.net/projects/compactfsm/


এটি 2018 সালে সন্ধান করা এবং এটি এখনও প্রযোজ্য। আমি @ প্যাক্সিডিয়াব্লো উত্তরটি পড়ছিলাম, এবং এম্বেড থাকা সিস্টেমে আমি সফলভাবে এই ধরণের প্রয়োগটি ব্যবহার করেছি। এই সমাধানটি প্যাক্সডিয়াব্লোস উত্তর থেকে অনুপস্থিত জিনিসগুলি যুক্ত করেছে :)
ক্রিস্টোফার

4

আর একটি আকর্ষণীয় ওপেন সোর্স সরঞ্জাম হ'ল রাজ্যচর্টস.আরজে ইয়াকিন্ডু স্টেটচার্ট সরঞ্জাম । এটি হেরেল স্টেটচার্টগুলি ব্যবহার করে এবং এইভাবে শ্রেণিবদ্ধ এবং সমান্তরাল রাজ্য সরবরাহ করে এবং সি এবং সি ++ (পাশাপাশি জাভা) কোড উত্পন্ন করে। এটি লাইব্রেরি ব্যবহার করে না তবে একটি 'প্লেইন কোড' পদ্ধতির অনুসরণ করে। কোডটি মূলত স্যুইচ-কেস স্ট্রাকচার প্রয়োগ করে। কোড জেনারেটর এছাড়াও কাস্টমাইজ করা যেতে পারে। অতিরিক্তভাবে সরঞ্জামটি আরও অনেকগুলি বৈশিষ্ট্য সরবরাহ করে।


3

এই দেরীতে (যথারীতি) আসছেন তবে তারিখের উত্তরগুলি স্ক্যান করে আমি মনে করি যে গুরুত্বপূর্ণ কিছু অনুপস্থিত;

আমি আমার নিজের প্রকল্পে দেখা গিয়েছে যে এটি খুব সহায়ক হতে পারে না যে বৈধ রাজ্য / ঘটনা সংযুক্তির জন্য একটি ফাংশন আছে। আমি কার্যকরভাবে রাজ্য / ইভেন্টের 2D টেবিল রাখার ধারণার মত করি। তবে আমি টেবিলের উপাদানগুলি একটি সাধারণ ফাংশন পয়েন্টারের চেয়ে বেশি হওয়া পছন্দ করি। এর পরিবর্তে আমি আমার নকশাটি সাজানোর চেষ্টা করি তাই একে একে একে একে একে একে একে খুব সহজেই পারমাণবিক উপাদান বা ক্রিয়া তৈরি করা যায়। এইভাবে আমি আমার রাজ্য / ইভেন্ট টেবিলের প্রতিটি মোড়ে সেই সাধারণ পারমাণবিক উপাদানগুলির তালিকা করতে পারি। ধারণাটি হ'ল আপনাকে এন স্কোয়ারের (সাধারণত খুব সাধারণ) ফাংশনগুলির একটি বৃহত সংজ্ঞা দিতে হবে না । এত ত্রুটিযুক্ত, সময় সাশ্রয়ী, লেখার পক্ষে কঠোর, পড়া শক্ত, কেন আপনি এর নাম রেখেছেন?

আমি একটি newচ্ছিক নতুন রাষ্ট্র এবং টেবিলের প্রতিটি কক্ষের জন্য একটি alচ্ছিক ফাংশন পয়েন্টারও অন্তর্ভুক্ত করি। ফাংশন পয়েন্টারটি সেই ব্যতিক্রমী ক্ষেত্রে রয়েছে যেখানে আপনি কেবল পারমাণবিক ক্রিয়াকলাপের একটি তালিকা বন্ধ করতে চান না।

আপনি জানেন যে আপনি ঠিক তখনই কাজ করছেন যখন আপনি লেখার জন্য কোনও নতুন কোড না দিয়ে কেবল আপনার টেবিলটি সম্পাদনা করে প্রচুর বিভিন্ন কার্যকারিতা প্রকাশ করতে পারেন।


2
সম্ভবত একটি উদাহরণ ভাল হবে, না?
jldupont

1
বিচ্ছিন্নতার মধ্যে উপস্থাপন করা যেতে পারে এমন একটি বাস্তব উদাহরণ হ'ল একটি চ্যালেঞ্জিং কাজ যা আমি এই মুহুর্তে দিতে প্রস্তুত হওয়ার চেয়ে আরও বেশি সময় প্রয়োজন। আমার পোস্টে এমন কিছু আছে যা বোঝার জন্য বিশেষত শক্ত? সম্ভবত আমি এটি আরও স্পষ্টভাবে প্রকাশ করতে পারি। ধারণাটি খুব সহজ; কোনও রাজ্য মেকানিজম সংজ্ঞায়িত করবেন না যা প্রতিটি ইভেন্ট / রাজ্যের সংমিশ্রণের জন্য পৃথক ফাংশন প্রয়োজন, আপনি সেভাবে অনেক বেশি ফাংশন পান। কমপক্ষে বেশিরভাগ ক্ষেত্রে, ইভেন্ট / রাজ্যের সংমিশ্রণের জন্য আপনি যে কার্যকারিতাটি চান তা বর্ণনা করার পরিবর্তে অন্য কোনও উপায় সন্ধান করুন।
বিল ফরস্টার

2
বোঝে: একটি সিউডো-কোড উদাহরণটি ভাল হত তবে আপনার বক্তব্য স্পষ্ট।
jldupont

3

অ্যালগার্ট, আমি মনে করি আমার অন্য সবার থেকে কিছুটা আলাদা। অন্যান্য উত্তরগুলিতে আমি যা দেখছি তার চেয়ে কোড এবং ডেটা থেকে একটু বেশি বিচ্ছেদ। এটি লেখার জন্য আমি তত্ত্বটি সত্যই পড়েছি, যা একটি পূর্ণ নিয়মিত-ভাষা প্রয়োগ করে (দুঃখের সাথে নিয়মিত প্রকাশ ছাড়াই)। ওলম্যান, মিনস্কি, চমস্কি। বলতে পারি না আমি এটি সব বুঝেছি, তবে আমি পুরানো মাস্টারদের কাছ থেকে যথাসম্ভব আঁকিয়েছি: তাদের কথায়।

আমি একটি ভবিষ্যদ্বাণীতে একটি ফাংশন পয়েন্টার ব্যবহার করি যা 'হ্যাঁ' অবস্থা বা 'না' অবস্থাতে স্থানান্তর নির্ধারণ করে। এটি আপনার নিয়মিত ভাষার জন্য সীমাবদ্ধ রাষ্ট্র গ্রহণকারীর তৈরিতে সহায়তা করে যা আপনি আরও সমাবেশ-ভাষা-জাতীয় পদ্ধতিতে প্রোগ্রাম করেন। আমার বোকা নাম পছন্দ দ্বারা বন্ধ করা হবে না দয়া করে। 'czek' == 'চেক'। 'গ্রুক' == [হ্যাকার অভিধানে এটি দেখুন]।

সুতরাং প্রতিটি পুনরাবৃত্তির জন্য, কারজেক আর্গুমেন্ট হিসাবে বর্তমান চরিত্রের সাথে একটি শিকারী ফাংশন কল করে। যদি ভবিষ্যদ্বাণীটি সত্যটি প্রত্যাবর্তন করে তবে চরিত্রটি গ্রাস হয়ে যায় (পয়েন্টারটি উন্নত) এবং আমরা পরবর্তী অবস্থা নির্বাচন করতে 'y' রূপান্তর অনুসরণ করি। যদি প্রিডিকেটটি মিথ্যা করে দেয় তবে চরিত্রটি গ্রাস হয় না এবং আমরা 'এন' রূপান্তরটি অনুসরণ করি follow তাই প্রতিটি নির্দেশই দ্বি-মুখী শাখা! আমি অবশ্যই মেল স্টোরি অফ মেল পড়েছিলাম।

এই কোডটি আমার পোস্টস্ক্রিপ্ট ইন্টারপ্রেটার থেকে সরাসরি আসে এবং এটি কমপ্লেক্স.এল.সি.সি. এর সহযোগীদের কাছ থেকে অনেক দিকনির্দেশনা সহ বর্তমান আকারে বিবর্তিত হয়েছিল যেহেতু পোস্টস্ক্রিপ্টের মূলত কোনও সিনট্যাক্স নেই (কেবলমাত্র ভারসাম্য বন্ধনীর প্রয়োজন), নিয়মিত ভাষা গ্রহণকারীও পার্সার হিসাবে এই ফাংশনগুলির মতো।

/* currentstr is set to the start of string by czek
   and used by setrad (called by israd) to set currentrad
   which is used by israddig to determine if the character
   in question is valid for the specified radix
   --
   a little semantic checking in the syntax!
 */
char *currentstr;
int currentrad;
void setrad(void) {
    char *end;
    currentrad = strtol(currentstr, &end, 10);
    if (*end != '#' /* just a sanity check,
                       the automaton should already have determined this */
    ||  currentrad > 36
    ||  currentrad < 2)
        fatal("bad radix"); /* should probably be a simple syntaxerror */
}

/*
   character classes
   used as tests by automatons under control of czek
 */
char *alpha = "0123456789" "ABCDE" "FGHIJ" "KLMNO" "PQRST" "UVWXYZ";
#define EQ(a,b) a==b
#define WITHIN(a,b) strchr(a,b)!=NULL
int israd  (int c) {
    if (EQ('#',c)) { setrad(); return true; }
    return false;
}
int israddig(int c) {
    return strchrnul(alpha,toupper(c))-alpha <= currentrad;
}
int isdot  (int c) {return EQ('.',c);}
int ise    (int c) {return WITHIN("eE",c);}
int issign (int c) {return WITHIN("+-",c);}
int isdel  (int c) {return WITHIN("()<>[]{}/%",c);}
int isreg  (int c) {return c!=EOF && !isspace(c) && !isdel(c);}
#undef WITHIN
#undef EQ

/*
   the automaton type
 */
typedef struct { int (*pred)(int); int y, n; } test;

/*
   automaton to match a simple decimal number
 */
/* /^[+-]?[0-9]+$/ */
test fsm_dec[] = {
/* 0*/ { issign,  1,  1 },
/* 1*/ { isdigit, 2, -1 },
/* 2*/ { isdigit, 2, -1 },
};
int acc_dec(int i) { return i==2; }

/*
   automaton to match a radix number
 */
/* /^[0-9]+[#][a-Z0-9]+$/ */
test fsm_rad[] = {
/* 0*/ { isdigit,  1, -1 },
/* 1*/ { isdigit,  1,  2 },
/* 2*/ { israd,    3, -1 },
/* 3*/ { israddig, 4, -1 },
/* 4*/ { israddig, 4, -1 },
};
int acc_rad(int i) { return i==4; }

/*
   automaton to match a real number
 */
/* /^[+-]?(d+(.d*)?)|(d*.d+)([eE][+-]?d+)?$/ */
/* represents the merge of these (simpler) expressions
   [+-]?[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?
   [+-]?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?
   The complexity comes from ensuring at least one
   digit in the integer or the fraction with optional
   sign and optional optionally-signed exponent.
   So passing isdot in state 3 means at least one integer digit has been found
   but passing isdot in state 4 means we must find at least one fraction digit
   via state 5 or the whole thing is a bust.
 */
test fsm_real[] = {
/* 0*/ { issign,  1,   1 },
/* 1*/ { isdigit, 2,   4 },
/* 2*/ { isdigit, 2,   3 },
/* 3*/ { isdot,   6,   7 },
/* 4*/ { isdot,   5,  -1 },
/* 5*/ { isdigit, 6,  -1 },
/* 6*/ { isdigit, 6,   7 },
/* 7*/ { ise,     8,  -1 },
/* 8*/ { issign,  9,   9 },
/* 9*/ { isdigit, 10, -1 },
/*10*/ { isdigit, 10, -1 },
};
int acc_real(int i) {
    switch(i) {
        case 2: /* integer */
        case 6: /* real */
        case 10: /* real with exponent */
            return true;
    }
    return false;
}

/*
   Helper function for grok.
   Execute automaton against the buffer,
   applying test to each character:
       on success, consume character and follow 'y' transition.
       on failure, do not consume but follow 'n' transition.
   Call yes function to determine if the ending state
   is considered an acceptable final state.
   A transition to -1 represents rejection by the automaton
 */
int czek (char *s, test *fsm, int (*yes)(int)) {
    int sta = 0;
    currentstr = s;
    while (sta!=-1 && *s) {
        if (fsm[sta].pred((int)*s)) {
            sta=fsm[sta].y;
            s++;
        } else {
            sta=fsm[sta].n;
        }
    }
    return yes(sta);
}

/*
   Helper function for toke.
   Interpret the contents of the buffer,
   trying automatons to match number formats;
   and falling through to a switch for special characters.
   Any token consisting of all regular characters
   that cannot be interpreted as a number is an executable name
 */
object grok (state *st, char *s, int ns,
    object *src,
    int (*next)(state *,object *),
    void (*back)(state *,int, object *)) {

    if (czek(s, fsm_dec, acc_dec)) {
        long num;
        num = strtol(s,NULL,10);
        if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) {
            error(st,limitcheck);
/*       } else if (num > INT_MAX || num < INT_MIN) { */
/*           error(limitcheck, OP_token); */
        } else {
            return consint(num);
        }
    }

    else if (czek(s, fsm_rad, acc_rad)) {
        long ra,num;
        ra = (int)strtol(s,NULL,10);
        if (ra > 36 || ra < 2) {
            error(st,limitcheck);
        }
        num = strtol(strchr(s,'#')+1, NULL, (int)ra);
        if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) {
            error(st,limitcheck);
/*       } else if (num > INT_MAX || num < INT_MAX) { */
/*           error(limitcheck, OP_token); */
        } else {
            return consint(num);
        }
    }

    else if (czek(s, fsm_real, acc_real)) {
        double num;
        num = strtod(s,NULL);
        if ((num==HUGE_VAL || num==-HUGE_VAL) && errno==ERANGE) {
            error(st,limitcheck);
        } else {
            return consreal(num);
        }
    }

    else switch(*s) {
        case '(': {
            int c, defer=1;
            char *sp = s;

            while (defer && (c=next(st,src)) != EOF ) {
                switch(c) {
                    case '(': defer++; break;
                    case ')': defer--;
                        if (!defer) goto endstring;
                        break;
                    case '\\': c=next(st,src);
                        switch(c) {
                            case '\n': continue;
                            case 'a': c = '\a'; break;
                            case 'b': c = '\b'; break;
                            case 'f': c = '\f'; break;
                            case 'n': c = '\n'; break;
                            case 'r': c = '\r'; break;
                            case 't': c = '\t'; break;
                            case 'v': c = '\v'; break;
                            case '\'': case '\"':
                            case '(': case ')':
                            default: break;
                        }
                }
                if (sp-s>ns) error(st,limitcheck);
                else *sp++ = c;
            }
endstring:  *sp=0;
            return cvlit(consstring(st,s,sp-s));
        }

        case '<': {
            int c;
            char d, *x = "0123456789abcdef", *sp = s;
            while (c=next(st,src), c!='>' && c!=EOF) {
                if (isspace(c)) continue;
                if (isxdigit(c)) c = strchr(x,tolower(c)) - x;
                else error(st,syntaxerror);
                d = (char)c << 4;
                while (isspace(c=next(st,src))) /*loop*/;
                if (isxdigit(c)) c = strchr(x,tolower(c)) - x;
                else error(st,syntaxerror);
                d |= (char)c;
                if (sp-s>ns) error(st,limitcheck);
                *sp++ = d;
            }
            *sp = 0;
            return cvlit(consstring(st,s,sp-s));
        }

        case '{': {
            object *a;
            size_t na = 100;
            size_t i;
            object proc;
            object fin;

            fin = consname(st,"}");
            (a = malloc(na * sizeof(object))) || (fatal("failure to malloc"),0);
            for (i=0 ; objcmp(st,a[i]=toke(st,src,next,back),fin) != 0; i++) {
                if (i == na-1)
                (a = realloc(a, (na+=100) * sizeof(object))) || (fatal("failure to malloc"),0);
            }
            proc = consarray(st,i);
            { size_t j;
                for (j=0; j<i; j++) {
                    a_put(st, proc, j, a[j]);
                }
            }
            free(a);
            return proc;
        }

        case '/': {
            s[1] = (char)next(st,src);
            puff(st, s+2, ns-2, src, next, back);
            if (s[1] == '/') {
                push(consname(st,s+2));
                opexec(st, op_cuts.load);
                return pop();
            }
            return cvlit(consname(st,s+1));
        }

        default: return consname(st,s);
    }
    return null; /* should be unreachable */
}

/*
   Helper function for toke.
   Read into buffer any regular characters.
   If we read one too many characters, put it back
   unless it's whitespace.
 */
int puff (state *st, char *buf, int nbuf,
    object *src,
    int (*next)(state *,object *),
    void (*back)(state *,int, object *)) {
    int c;
    char *s = buf;
    while (isreg(c=next(st,src))) {
        if (s-buf >= nbuf-1) return false;
        *s++ = c;
    }
    *s = 0;
    if (!isspace(c) && c != EOF) back(st,c,src); /* eat interstice */
    return true;
}

/*
   Helper function for Stoken Ftoken.
   Read a token from src using next and back.
   Loop until having read a bona-fide non-whitespace non-comment character.
   Call puff to read into buffer up to next delimiter or space.
   Call grok to figure out what it is.
 */
#define NBUF MAXLINE
object toke (state *st, object *src,
        int (*next)(state *, object *),
        void (*back)(state *, int, object *)) {
    char buf[NBUF] = "", *s=buf;
    int c,sta = 1;
    object o;

    do {
        c=next(st,src);
        //if (c==EOF) return null;
        if (c=='%') {
            if (DUMPCOMMENTS) fputc(c, stdout);
            do {
                c=next(st,src);
                if (DUMPCOMMENTS) fputc(c, stdout);
            } while (c!='\n' && c!='\f' && c!=EOF);
        }
    } while (c!=EOF && isspace(c));
    if (c==EOF) return null;
    *s++ = c;
    *s = 0;
    if (!isdel(c)) sta=puff(st, s,NBUF-1,src,next,back);

    if (sta) {
        o=grok(st,buf,NBUF-1,src,next,back);
        return o;
    } else {
        return null;
    }
}

2
কোনও পার্সার বা লেক্সার জেনারেটর আপনার জন্য আনন্দের সাথে নির্গত করবে। অশুভভাবে তাই। আপনি নিজের হাতে এটি কোড আপ করতে চান কিনা তা প্রশ্নবিদ্ধ। অবশ্যই এটি শিক্ষাগত যোগ্যতা আছে।
মনিকা পুনরায় ইনস্টল করুন

3

boost.org 2 টি পৃথক রাষ্ট্রীয় চার্ট বাস্তবায়নের সাথে আসে:

সর্বদা হিসাবে, বুস্ট আপনাকে টেমপ্লেট নরকে রূপ দেবে।

প্রথম গ্রন্থাগারটি আরও কর্মক্ষমতা-সমালোচনামূলক রাষ্ট্র মেশিনগুলির জন্য। দ্বিতীয় লাইব্রেরি আপনাকে একটি ইউএমএল স্টেটচার্ট থেকে কোডে সরাসরি संक्रमणের পথ দেয়।

এখানে উভয় লেখকের উভয় প্রতিক্রিয়া যেখানে দুটি মধ্যে একটি তুলনা জিজ্ঞাসা করতে এসও প্রশ্ন এখানে ।


2

কিছুটা জটিল বিট কন্ট্রোল যুক্ত সম্পর্কে আরস ওপেনফোরাম পোস্টগুলির এই সিরিজটিতে সিতে একটি রাষ্ট্রীয় মেশিন হিসাবে একটি খুব সহজে অনুসরণ করা অনুসরণ বাস্তবায়ন অন্তর্ভুক্ত includes


2

এটা কোথাও দেখেছি

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0)
      NEXTSTATE(y);
    else
      NEXTSTATE(x);
  }
}

1
এটি আকর্ষণীয়, তবে যতক্ষণ না আপনি দুটি বা উদাহরণ (এবং সম্ভবত ডি-ম্যাক্রোয়েড ফলাফল) না দেন বা কোনও কারণে কেন এটি অন্যের চেয়ে বেশি ব্যবহারিক হতে পারে সে সম্পর্কে কিছু আলোচনা না করা পর্যন্ত কোনও আপত্তি নেই। এতিম বন্ধনী এবং ম্যাক্রোর আকর্ষণীয় ব্যবহার। আমি ভাবছি অনুরূপ কিছু এমন ভাষায় করা যেতে পারে যা কিছু ধরণের লেজ পুনরাবৃত্তি অপ্টিমাইজেশন করে; আপনি সরাসরি ফাংশন কলগুলি ব্যবহার করতে পারেন এবং ফাংশন কল আবর্জনা (যা আমি মনে করি ম্যাক্রোগুলি এখানে মূলত কাটিয়ে উঠেছে) এর সাথে স্ট্যাক স্পেসটি ওভারলোড করার বিষয়ে চিন্তা করবেন না)
উঠেছে

2
এই পদ্ধতির সুবিধাগুলি ...? আমি বেশ কয়েকটি অসুবিধাগুলি দেখতে পাচ্ছি, যেমন ম্যাক্রোকে gotoঅবহেলা করা এবং এর ব্যবহারটি একটি প্রাকৃতিক মাল্টিটাস্কিং ওএসের উপর নির্ভরতা তৈরি করে।
ক্রেগ ম্যাককুইন

2

আপনি বোঝাচ্ছেন যে আপনি সি ++ ব্যবহার করতে পারেন এবং সুতরাং ওও কোডটি ব্যবহার করতে পারেন, আমি 'GoF'state প্যাটার্নটি মূল্যায়ন করার পরামর্শ দেব (GoF = গ্যাং অফ ফোর, যে ছেলেরা ডিজাইনের প্যাটার্ন বইটি লিখেছিল যা নকশার নিদর্শনগুলিকে লাইমলাইটে নিয়ে আসে)।

এটি বিশেষত জটিল নয় এবং এটি ব্যাপকভাবে ব্যবহৃত হয় এবং আলোচিত হয় তাই লাইনে উদাহরণ এবং ব্যাখ্যা দেখতে সহজ।

এটি সম্ভবত পরবর্তী কোনও সময়ে আপনার কোড বজায় রেখে অন্য কারও দ্বারা স্বীকৃত হবে।

দক্ষতা যদি উদ্বেগের বিষয় হয় তবে প্রচুর উপাদানগুলি কার্য সম্পাদনকে প্রভাবিত করে এবং এটি সর্বদা ওও খারাপ, কার্যকরী কোড ভাল নয় এমনটি নিশ্চিত করার জন্য এটি বেঞ্চমার্কিংয়ের পক্ষে মূল্যবান হবে যে প্রচুর পরিমাণে কার্যকারিতা প্রভাবিত হয় এবং এটি কোনও OO পদ্ধতির চেয়ে বেশি দক্ষ। একইভাবে, যদি মেমরির ব্যবহার আপনার জন্য বাধা হয়ে থাকে তবে আপনি কিছু পরীক্ষা বা গণনা করা আবার যদি আপনি রাষ্ট্রের প্যাটার্নটি ব্যবহার করেন তবে এটি আপনার নির্দিষ্ট অ্যাপ্লিকেশনটির পক্ষে আসলে একটি সমস্যা হয়ে দাঁড়াবে কিনা তা দেখার দরকার।

নিম্নলিখিত ক্রেগ পরামর্শ হিসাবে 'গোফ' রাষ্ট্রীয় প্যাটার্ন কিছু লিঙ্ক আছে:


একটি মন্তব্যের মতো দেখতে আরও বেশি: আমি কি আপনাকে এটির মতো আচরণ করার পরামর্শ দিতে পারি? যেমন এটি "উত্তর" বিভাগে রাখবেন না।
jldupont

আপনি যদি এটির সাথে পরিচিত না হন তাদের জন্য "GoF রাষ্ট্রীয় প্যাটার্ন" এর জন্য একটি ভাল URL লিঙ্ক সরবরাহ করতে পারেন তবে এটি ভাল।
ক্রেগ ম্যাককুইন

1
@jldupont - ন্যায্য মন্তব্য। আমি ব্যক্তিগত অভিজ্ঞতার উপর ভিত্তি করে অনুভব করি যেহেতু আমি এটির যথাযথ উত্তর দেওয়ার জন্য পাঠ্যটি পরিবর্তন করেছি যে নির্দিষ্ট পারফরম্যান্সের সমস্যা না থাকলে, জিওএফ পদ্ধতির কাজ ভাল হয় এবং অপেক্ষাকৃত বড় 'ব্যবহারকারী বেস' থাকবে
মিক

@ ক্রেইগ - কিছু লিঙ্ক যুক্ত হয়েছে। আমি এগুলি যুক্ত করার সময় উভয়ই সঠিক এবং স্পষ্ট দেখতে পেয়েছিল।
মিক

2

এখানে লিনাক্সের জন্য একটি ফাইনাইট স্টেট মেশিনের উদাহরণ রয়েছে যা ইভেন্ট হিসাবে বার্তা সারি ব্যবহার করে। ইভেন্টগুলি কাতারে রাখা হয় এবং পরিচালনা করা হয়। প্রতিটি ইভেন্টের জন্য কী ঘটে তার উপর নির্ভর করে রাষ্ট্র পরিবর্তন করে।

রাষ্ট্রগুলির সাথে ডেটা সংযোগের জন্য এটি উদাহরণ:

  • uninitialized
  • সক্রিয়া
  • সংযুক্ত
  • এমটিইউ আলোচনা করেছেন
  • প্রমাণীকৃত

আমি যুক্ত একটি সামান্য অতিরিক্ত বৈশিষ্ট্য প্রতিটি বার্তা / ইভেন্টের জন্য একটি টাইমস্ট্যাম্প ছিল। ইভেন্ট হ্যান্ডলার খুব পুরানো ইভেন্টগুলির উপেক্ষা করবে (তাদের মেয়াদ শেষ হয়ে গেছে)। এটি আসল বিশ্বে অনেক কিছুই ঘটতে পারে যেখানে আপনি অপ্রত্যাশিত অবস্থায় আটকে যেতে পারেন।

এই উদাহরণটি লিনাক্সে চলে, এটি সংকলন করতে নীচের মেকফিলটি ব্যবহার করুন এবং এটির সাথে খেলুন।

state_machine.c

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>   // sysconf()
#include <errno.h>    // errno
#include <string.h>   // strerror()
#include <sys/time.h> // gettimeofday()
#include <fcntl.h>    // For O_* constants
#include <sys/stat.h> // For mode constants

#include <mqueue.h>
#include <poll.h>

//------------------------------------------------
// States
//------------------------------------------------
typedef enum
{
    ST_UNKNOWN = 0,
    ST_UNINIT,
    ST_INIT,
    ST_CONNECTED,
    ST_MTU_NEGOTIATED,
    ST_AUTHENTICATED,
    ST_ERROR,
    ST_DONT_CHANGE,
    ST_TERM,
} fsmState_t;

//------------------------------------------------
// Events
//------------------------------------------------
typedef enum
{
    EV_UNKNOWN = 0,
    EV_INIT_SUCCESS,
    EV_INIT_FAIL,
    EV_MASTER_CMD_MSG,
    EV_CONNECT_SUCCESS,
    EV_CONNECT_FAIL,
    EV_MTU_SUCCESS,
    EV_MTU_FAIL,
    EV_AUTH_SUCCESS,
    EV_AUTH_FAIL,
    EV_TX_SUCCESS,
    EV_TX_FAIL,
    EV_DISCONNECTED,
    EV_DISCON_FAILED,
    EV_LAST_ENTRY,
} fsmEvName_t;

typedef struct fsmEvent_type
{
    fsmEvName_t name;
    struct timeval genTime; // Time the event was generated.
                            // This allows us to see how old the event is.
} fsmEvent_t;

// Finite State Machine Data Members
typedef struct fsmData_type
{
    int  connectTries;
    int  MTUtries;
    int  authTries;
    int  txTries;
} fsmData_t;

// Each row of the state table
typedef struct stateTable_type {
    fsmState_t  st;             // Current state
    fsmEvName_t evName;         // Got this event
    int (*conditionfn)(void *);  // If this condition func returns TRUE
    fsmState_t nextState;       // Change to this state and
    void (*fn)(void *);          // Run this function
} stateTable_t;

// Finite State Machine state structure
typedef struct fsm_type
{
    const stateTable_t *pStateTable; // Pointer to state table
    int        numStates;            // Number of entries in the table
    fsmState_t currentState;         // Current state
    fsmEvent_t currentEvent;         // Current event
    fsmData_t *fsmData;              // Pointer to the data attributes
    mqd_t      mqdes;                // Message Queue descriptor
    mqd_t      master_cmd_mqdes;     // Master command message queue
} fsm_t;

// Wildcard events and wildcard state
#define   EV_ANY    -1
#define   ST_ANY    -1
#define   TRUE     (1)
#define   FALSE    (0)

// Maximum priority for message queues (see "man mq_overview")
#define FSM_PRIO  (sysconf(_SC_MQ_PRIO_MAX) - 1)

static void addev                              (fsm_t *fsm, fsmEvName_t ev);
static void doNothing                          (void *fsm) {addev(fsm, EV_MASTER_CMD_MSG);}
static void doInit                             (void *fsm) {addev(fsm, EV_INIT_SUCCESS);}
static void doConnect                          (void *fsm) {addev(fsm, EV_CONNECT_SUCCESS);}
static void doMTU                              (void *fsm) {addev(fsm, EV_MTU_SUCCESS);}
static void reportFailConnect                  (void *fsm) {addev(fsm, EV_ANY);}
static void doAuth                             (void *fsm) {addev(fsm, EV_AUTH_SUCCESS);}
static void reportDisConnect                   (void *fsm) {addev(fsm, EV_ANY);}
static void doDisconnect                       (void *fsm) {addev(fsm, EV_ANY);}
static void doTransaction                      (void *fsm) {addev(fsm, EV_TX_FAIL);}
static void fsmError                           (void *fsm) {addev(fsm, EV_ANY);}

static int currentlyLessThanMaxConnectTries    (void *fsm) {
    fsm_t *l = (fsm_t *)fsm;
    return (l->fsmData->connectTries < 5 ? TRUE : FALSE);
}
static int        isMoreThanMaxConnectTries    (void *fsm) {return TRUE;}
static int currentlyLessThanMaxMTUtries        (void *fsm) {return TRUE;}
static int        isMoreThanMaxMTUtries        (void *fsm) {return TRUE;}
static int currentyLessThanMaxAuthTries        (void *fsm) {return TRUE;}
static int       isMoreThanMaxAuthTries        (void *fsm) {return TRUE;}
static int currentlyLessThanMaxTXtries         (void *fsm) {return FALSE;}
static int        isMoreThanMaxTXtries         (void *fsm) {return TRUE;}
static int didNotSelfDisconnect                (void *fsm) {return TRUE;}

static int  waitForEvent                       (fsm_t *fsm);
static void runEvent                           (fsm_t *fsm);
static void runStateMachine(fsm_t *fsm);
static int newEventIsValid(fsmEvent_t *event);
static void getTime(struct timeval *time);
void printState(fsmState_t st);
void printEvent(fsmEvName_t ev);

// Global State Table
const stateTable_t GST[] = {
    // Current state         Got this event          If this condition func returns TRUE     Change to this state and    Run this function
    { ST_UNINIT,             EV_INIT_SUCCESS,        NULL,                                   ST_INIT,                    &doNothing              },
    { ST_UNINIT,             EV_INIT_FAIL,           NULL,                                   ST_UNINIT,                  &doInit                 },
    { ST_INIT,               EV_MASTER_CMD_MSG,      NULL,                                   ST_INIT,                    &doConnect              },
    { ST_INIT,               EV_CONNECT_SUCCESS,     NULL,                                   ST_CONNECTED,               &doMTU                  },
    { ST_INIT,               EV_CONNECT_FAIL,        &currentlyLessThanMaxConnectTries,      ST_INIT,                    &doConnect              },
    { ST_INIT,               EV_CONNECT_FAIL,        &isMoreThanMaxConnectTries,             ST_INIT,                    &reportFailConnect      },
    { ST_CONNECTED,          EV_MTU_SUCCESS,         NULL,                                   ST_MTU_NEGOTIATED,          &doAuth                 },
    { ST_CONNECTED,          EV_MTU_FAIL,            &currentlyLessThanMaxMTUtries,          ST_CONNECTED,               &doMTU                  },
    { ST_CONNECTED,          EV_MTU_FAIL,            &isMoreThanMaxMTUtries,                 ST_CONNECTED,               &doDisconnect           },
    { ST_CONNECTED,          EV_DISCONNECTED,        &didNotSelfDisconnect,                  ST_INIT,                    &reportDisConnect       },
    { ST_MTU_NEGOTIATED,     EV_AUTH_SUCCESS,        NULL,                                   ST_AUTHENTICATED,           &doTransaction          },
    { ST_MTU_NEGOTIATED,     EV_AUTH_FAIL,           &currentyLessThanMaxAuthTries,          ST_MTU_NEGOTIATED,          &doAuth                 },
    { ST_MTU_NEGOTIATED,     EV_AUTH_FAIL,           &isMoreThanMaxAuthTries,                ST_MTU_NEGOTIATED,          &doDisconnect           },
    { ST_MTU_NEGOTIATED,     EV_DISCONNECTED,        &didNotSelfDisconnect,                  ST_INIT,                    &reportDisConnect       },
    { ST_AUTHENTICATED,      EV_TX_SUCCESS,          NULL,                                   ST_AUTHENTICATED,           &doDisconnect           },
    { ST_AUTHENTICATED,      EV_TX_FAIL,             &currentlyLessThanMaxTXtries,           ST_AUTHENTICATED,           &doTransaction          },
    { ST_AUTHENTICATED,      EV_TX_FAIL,             &isMoreThanMaxTXtries,                  ST_AUTHENTICATED,           &doDisconnect           },
    { ST_AUTHENTICATED,      EV_DISCONNECTED,        &didNotSelfDisconnect,                  ST_INIT,                    &reportDisConnect       },
    { ST_ANY,                EV_DISCON_FAILED,       NULL,                                   ST_DONT_CHANGE,             &doDisconnect           },
    { ST_ANY,                EV_ANY,                 NULL,                                   ST_UNINIT,                  &fsmError               }    // Wildcard state for errors
};

#define GST_COUNT (sizeof(GST)/sizeof(stateTable_t))

int main()
{
    int ret = 0;
    fsmData_t dataAttr;
    dataAttr.connectTries = 0;
    dataAttr.MTUtries     = 0;
    dataAttr.authTries    = 0;
    dataAttr.txTries      = 0;

    fsm_t lfsm;
    memset(&lfsm, 0, sizeof(fsm_t));
    lfsm.pStateTable       = GST;
    lfsm.numStates         = GST_COUNT;
    lfsm.currentState      = ST_UNINIT;
    lfsm.currentEvent.name = EV_ANY;
    lfsm.fsmData           = &dataAttr;

    struct mq_attr attr;
    attr.mq_maxmsg = 30;
    attr.mq_msgsize = sizeof(fsmEvent_t);

    // Dev info
    //printf("Size of fsmEvent_t [%ld]\n", sizeof(fsmEvent_t));

    ret = mq_unlink("/abcmq");
    if (ret == -1) {
        fprintf(stderr, "Error on mq_unlink(), errno[%d] strerror[%s]\n",
                errno, strerror(errno));
    }

    lfsm.mqdes = mq_open("/abcmq", O_CREAT | O_RDWR, S_IWUSR | S_IRUSR, &attr);
    if (lfsm.mqdes == (mqd_t)-1) {
        fprintf(stderr, "Error on mq_open(), errno[%d] strerror[%s]\n",
                errno, strerror(errno));
        return -1;
    }

    doInit(&lfsm);  // This will generate the first event
    runStateMachine(&lfsm);

    return 0;
}


static void runStateMachine(fsm_t *fsm)
{
    int ret = 0;

    if (fsm == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return;
    }

    // Cycle through the state machine
    while (fsm->currentState != ST_TERM) {
        printf("current state [");
        printState(fsm->currentState);
        printf("]\n");

        ret = waitForEvent(fsm);
        if (ret == 0) {
            printf("got event [");
            printEvent(fsm->currentEvent.name);
            printf("]\n");

            runEvent(fsm);
        }
        sleep(2);
    }
}


static int waitForEvent(fsm_t *fsm)
{
    //const int numFds = 2;
    const int numFds = 1;
    struct pollfd fds[numFds];
    int timeout_msecs = -1; // -1 is forever
    int ret = 0;
    int i = 0;
    ssize_t num = 0;
    fsmEvent_t newEv;

    if (fsm == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return -1;
    }

    fsm->currentEvent.name = EV_ANY;

    fds[0].fd     = fsm->mqdes;
    fds[0].events = POLLIN;
    //fds[1].fd     = fsm->master_cmd_mqdes;
    //fds[1].events = POLLIN;
    ret = poll(fds, numFds, timeout_msecs);

    if (ret > 0) {
        // An event on one of the fds has occurred
        for (i = 0; i < numFds; i++) {
            if (fds[i].revents & POLLIN) {
                // Data may be read on device number i
                num = mq_receive(fds[i].fd, (void *)(&newEv),
                                 sizeof(fsmEvent_t), NULL);
                if (num == -1) {
                    fprintf(stderr, "Error on mq_receive(), errno[%d] "
                            "strerror[%s]\n", errno, strerror(errno));
                    return -1;
                }

                if (newEventIsValid(&newEv)) {
                    fsm->currentEvent = newEv;
                } else {
                    return -1;
                }
            }
        }
    } else {
        fprintf(stderr, "Error on poll(), ret[%d] errno[%d] strerror[%s]\n",
                ret, errno, strerror(errno));
        return -1;
    }

    return 0;
}


static int newEventIsValid(fsmEvent_t *event)
{
    if (event == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return FALSE;
    }

    printf("[%s]\n", __func__);

    struct timeval now;
    getTime(&now);

    if ( (event->name < EV_LAST_ENTRY) &&
         ((now.tv_sec - event->genTime.tv_sec) < (60*5))
       )
    {
        return TRUE;
    } else {
        return FALSE;
    }
}


//------------------------------------------------
// Performs event handling on the FSM (finite state machine).
// Make sure there is a wildcard state at the end of
// your table, otherwise; the event will be ignored.
//------------------------------------------------
static void runEvent(fsm_t *fsm)
{
    int i;
    int condRet = 0;

    if (fsm == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return;
    }

    printf("[%s]\n", __func__);

    // Find a relevant entry for this state and event
    for (i = 0; i < fsm->numStates; i++) {
        // Look in the table for our current state or ST_ANY
        if (  (fsm->pStateTable[i].st == fsm->currentState) ||
              (fsm->pStateTable[i].st == ST_ANY)
           )
        {
            // Is this the event we are looking for?
            if ( (fsm->pStateTable[i].evName == fsm->currentEvent.name) ||
                 (fsm->pStateTable[i].evName == EV_ANY)
               )
            {
                if (fsm->pStateTable[i].conditionfn != NULL) {
                    condRet = fsm->pStateTable[i].conditionfn(fsm->fsmData);
                }

                // See if there is a condition associated
                // or we are not looking for any condition
                //
                if ( (condRet != 0) || (fsm->pStateTable[i].conditionfn == NULL))
                {
                    // Set the next state (if applicable)
                    if (fsm->pStateTable[i].nextState != ST_DONT_CHANGE) {
                        fsm->currentState = fsm->pStateTable[i].nextState;
                        printf("new state [");
                        printState(fsm->currentState);
                        printf("]\n");
                    }

                    // Call the state callback function
                    fsm->pStateTable[i].fn(fsm);
                    break;
                }
            }
        }
    }
}


//------------------------------------------------
//               EVENT HANDLERS
//------------------------------------------------
static void getTime(struct timeval *time)
{
    if (time == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return;
    }

    printf("[%s]\n", __func__);

    int ret = gettimeofday(time, NULL);
    if (ret != 0) {
        fprintf(stderr, "gettimeofday() failed: errno [%d], strerror [%s]\n",
                errno, strerror(errno));
        memset(time, 0, sizeof(struct timeval));
    }
}


static void addev (fsm_t *fsm, fsmEvName_t ev)
{
    int ret = 0;

    if (fsm == NULL) {
        fprintf(stderr, "[%s] NULL argument\n", __func__);
        return;
    }

    printf("[%s] ev[%d]\n", __func__, ev);

    if (ev == EV_ANY) {
        // Don't generate a new event, just return...
        return;
    }

    fsmEvent_t newev;
    getTime(&(newev.genTime));
    newev.name = ev;

    ret = mq_send(fsm->mqdes, (void *)(&newev), sizeof(fsmEvent_t), FSM_PRIO);
    if (ret == -1) {
        fprintf(stderr, "[%s] mq_send() failed: errno [%d], strerror [%s]\n",
                __func__, errno, strerror(errno));
    }
}
//------------------------------------------------
//           end EVENT HANDLERS
//------------------------------------------------

void printState(fsmState_t st)
{
    switch(st) {
        case    ST_UNKNOWN:
        printf("ST_UNKNOWN");
            break;
        case    ST_UNINIT:
        printf("ST_UNINIT");
            break;
        case    ST_INIT:
        printf("ST_INIT");
            break;
        case    ST_CONNECTED:
        printf("ST_CONNECTED");
            break;
        case    ST_MTU_NEGOTIATED:
        printf("ST_MTU_NEGOTIATED");
            break;
        case    ST_AUTHENTICATED:
        printf("ST_AUTHENTICATED");
            break;
        case    ST_ERROR:
        printf("ST_ERROR");
            break;
        case    ST_TERM:
        printf("ST_TERM");
            break;
        default:
        printf("unknown state");
            break;
    }
}

void printEvent(fsmEvName_t ev)
{
    switch (ev) {
        case    EV_UNKNOWN:
        printf("EV_UNKNOWN");
            break;
        case    EV_INIT_SUCCESS:
        printf("EV_INIT_SUCCESS");
            break;
        case    EV_INIT_FAIL:
        printf("EV_INIT_FAIL");
            break;
        case    EV_MASTER_CMD_MSG:
        printf("EV_MASTER_CMD_MSG");
            break;
        case    EV_CONNECT_SUCCESS:
        printf("EV_CONNECT_SUCCESS");
            break;
        case    EV_CONNECT_FAIL:
        printf("EV_CONNECT_FAIL");
            break;
        case    EV_MTU_SUCCESS:
        printf("EV_MTU_SUCCESS");
            break;
        case    EV_MTU_FAIL:
        printf("EV_MTU_FAIL");
            break;
        case    EV_AUTH_SUCCESS:
        printf("EV_AUTH_SUCCESS");
            break;
        case    EV_AUTH_FAIL:
        printf("EV_AUTH_FAIL");
            break;
        case    EV_TX_SUCCESS:
        printf("EV_TX_SUCCESS");
            break;
        case    EV_TX_FAIL:
        printf("EV_TX_FAIL");
            break;
        case    EV_DISCONNECTED:
        printf("EV_DISCONNECTED");
            break;
        case    EV_LAST_ENTRY:
        printf("EV_LAST_ENTRY");
            break;
        default:
        printf("unknown event");
            break;
    }
}

Makefile

CXX = gcc
COMPFLAGS = -c -Wall -g

state_machine: state_machine.o
    $(CXX) -lrt state_machine.o -o state_machine

state_machine.o: state_machine.c
    $(CXX) $(COMPFLAGS) state_machine.c

clean:
    rm state_machine state_machine.o

1

আপনার প্রশ্নটি বেশ জেনারিক,
এখানে দুটি তথ্যসূত্র নিবন্ধ যা কার্যকর হতে পারে,

  1. এম্বেড থাকা রাজ্য মেশিন বাস্তবায়ন

    এই নিবন্ধটি এম্বেড থাকা সিস্টেমের জন্য একটি রাষ্ট্র মেশিন বাস্তবায়নের সহজ পদ্ধতির বর্ণনা দেয় describes এই নিবন্ধটির উদ্দেশ্যে, একটি রাষ্ট্রীয় মেশিনকে একটি অ্যালগরিদম হিসাবে সংজ্ঞায়িত করা হয় যা অল্প সংখ্যক রাজ্যের মধ্যে একটি হতে পারে। একটি রাষ্ট্র এমন একটি শর্ত যা আউটপুটগুলিতে ইনপুটগুলির সাথে নির্ধারিত সম্পর্কের এবং পরবর্তী রাজ্যে ইনপুটগুলির জন্য নির্ধারিত সম্পর্ক সৃষ্টি করে।
    একজন সচেতন পাঠক দ্রুত লক্ষ করবেন যে এই নিবন্ধে বর্ণিত রাষ্ট্রীয় মেশিনগুলি মেলা মেশিন। মেলি মেশিন এমন একটি রাষ্ট্রীয় মেশিন যেখানে আউটপুটগুলি মুর মেশিনের বিপরীতে উপস্থিত রাষ্ট্র এবং ইনপুট উভয়েরই ফাংশন, যেখানে আউটপুটগুলি কেবলমাত্র রাষ্ট্রের কাজ function

    • সি এবং সি ++ এ স্টেট মেশিনগুলির কোডিং

      এই নিবন্ধটিতে আমার ব্যস্ততা সিটি বা সি ++ এ স্টেট মেশিন কোডিংয়ের জন্য স্টেট-মেশিন ফান্ডামেন্টাল এবং কিছু সোজা প্রোগ্রামিং গাইডলাইন সহ। আমি আশা করি যে এই সাধারণ কৌশলগুলি আরও সাধারণ হয়ে উঠতে পারে, যাতে আপনি (এবং অন্যরা) উত্স কোড থেকে রাষ্ট্র-মেশিন কাঠামোটি সহজেই দেখতে পান।



1

এটি প্রচুর উত্তর সহ একটি পুরাতন পোস্ট, তবে আমি ভেবেছিলাম যে আমি সি এর সসীম স্টেট মেশিনে আমার নিজস্ব পন্থা যুক্ত করব যে কোনও সংখ্যক রাজ্যের কঙ্কাল সি কোড তৈরি করার জন্য আমি পাইথন লিপি তৈরি করেছি। এই স্ক্রিপ্টটি FsmTemplateC এ গিটুহাবের নথিভুক্ত করা হয়েছে

এই উদাহরণটি আমি যে অন্য পদ্ধতির বিষয়ে পড়েছি তার উপর ভিত্তি করে। এটি গোটো বা স্যুইচ স্টেটমেন্ট ব্যবহার করে না তবে পরিবর্তে পয়েন্টার ম্যাট্রিক্সে রূপান্তর ফাংশন রয়েছে (সন্ধানী টেবিল)। কোডটি একটি বড় মাল্টি-লাইন ইনিশিয়ালাইজার ম্যাক্রো এবং সি 99 বৈশিষ্ট্যগুলির উপর নির্ভর করে (মনোনীত প্রারম্ভিক এবং যৌগিক আক্ষরিক) যাতে আপনি এই জিনিসগুলি পছন্দ না করেন তবে আপনি এই পদ্ধতির পছন্দ নাও করতে পারেন।

এখানে টার্নস্টাইল উদাহরণের পাইথন স্ক্রিপ্ট রয়েছে যা FsmTemplateC ব্যবহার করে কঙ্কাল সি-কোড উত্পন্ন করে :

# dict parameter for generating FSM
fsm_param = {
    # main FSM struct type string
    'type': 'FsmTurnstile',
    # struct type and name for passing data to state machine functions
    # by pointer (these custom names are optional)
    'fopts': {
        'type': 'FsmTurnstileFopts',
        'name': 'fopts'
    },
    # list of states
    'states': ['locked', 'unlocked'],
    # list of inputs (can be any length > 0)
    'inputs': ['coin', 'push'],
    # map inputs to commands (next desired state) using a transition table
    # index of array corresponds to 'inputs' array
    # for this example, index 0 is 'coin', index 1 is 'push'
    'transitiontable': {
        # current state |  'coin'  |  'push'  |
        'locked':       ['unlocked',        ''],
        'unlocked':     [        '',  'locked']
    }
}

# folder to contain generated code
folder = 'turnstile_example'
# function prefix
prefix = 'fsm_turnstile'

# generate FSM code
code = fsm.Fsm(fsm_param).genccode(folder, prefix)

উত্পন্ন আউটপুট শিরোনামে টাইপিডগুলি রয়েছে:

/* function options (EDIT) */
typedef struct FsmTurnstileFopts {
    /* define your options struct here */
} FsmTurnstileFopts;

/* transition check */
typedef enum eFsmTurnstileCheck {
    EFSM_TURNSTILE_TR_RETREAT,
    EFSM_TURNSTILE_TR_ADVANCE,
    EFSM_TURNSTILE_TR_CONTINUE,
    EFSM_TURNSTILE_TR_BADINPUT
} eFsmTurnstileCheck;

/* states (enum) */
typedef enum eFsmTurnstileState {
    EFSM_TURNSTILE_ST_LOCKED,
    EFSM_TURNSTILE_ST_UNLOCKED,
    EFSM_TURNSTILE_NUM_STATES
} eFsmTurnstileState;

/* inputs (enum) */
typedef enum eFsmTurnstileInput {
    EFSM_TURNSTILE_IN_COIN,
    EFSM_TURNSTILE_IN_PUSH,
    EFSM_TURNSTILE_NUM_INPUTS,
    EFSM_TURNSTILE_NOINPUT
} eFsmTurnstileInput;

/* finite state machine struct */
typedef struct FsmTurnstile {
    eFsmTurnstileInput input;
    eFsmTurnstileCheck check;
    eFsmTurnstileState cur;
    eFsmTurnstileState cmd;
    eFsmTurnstileState **transition_table;
    void (***state_transitions)(struct FsmTurnstile *, FsmTurnstileFopts *);
    void (*run)(struct FsmTurnstile *, FsmTurnstileFopts *, const eFsmTurnstileInput);
} FsmTurnstile;

/* transition functions */
typedef void (*pFsmTurnstileStateTransitions)(struct FsmTurnstile *, FsmTurnstileFopts *);
  • এনুম eFsmTurnstileCheckব্যবহার করে একটি সংক্রমণ অবরুদ্ধ ছিল EFSM_TURNSTILE_TR_RETREAT, সঙ্গে অগ্রগতির অনুমতি দেওয়া হয়েছিল EFSM_TURNSTILE_TR_ADVANCEবা ফাংশন কলটি এর আগে রূপান্তরিত হয়েছিল কিনা তা নির্ধারণ করতে ব্যবহৃত হয় EFSM_TURNSTILE_TR_CONTINUE
  • enum eFsmTurnstileStateকেবল রাজ্যের তালিকা।
  • এনাম eFsmTurnstileInputকেবল ইনপুটগুলির তালিকা।
  • FsmTurnstileStruct হয় রূপান্তরটি চেক, ফাংশন লুকআপ টেবিল বর্তমান অবস্থা হুকুম রাষ্ট্র, এবং প্রাথমিক কাজ যে মেশিন চালায় একটি ওরফে সঙ্গে রাষ্ট্র মেশিন এর হৃদয়।
  • প্রতিটি ফাংশন পয়েন্টার (ওরফে) FsmTurnstileকেবল স্ট্রাক্ট থেকে কল করা উচিত এবং নিজের পয়েন্টার হিসাবে এটির প্রথম ইনপুটটি নিজের কাছে রাখতে হবে যাতে স্থির অবস্থা, অবজেক্ট-ভিত্তিক স্টাইল বজায় রাখা যায়।

এখন শিরোনামে ফাংশন ঘোষণার জন্য:

/* fsm declarations */
void fsm_turnstile_locked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_locked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_unlocked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_unlocked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts);
void fsm_turnstile_run (FsmTurnstile *fsm, FsmTurnstileFopts *fopts, const eFsmTurnstileInput input);

ফাংশনের নামগুলি ফর্ম্যাটে রয়েছে {prefix}_{from}_{to}যেখানে {from}পূর্বের (বর্তমান) অবস্থা এবং {to}পরের রাজ্যটি। দ্রষ্টব্য যে যদি রূপান্তর টেবিলটি নির্দিষ্ট রূপান্তরের জন্য অনুমতি না দেয় তবে একটি ফাংশন পয়েন্টারের পরিবর্তে একটি NULL পয়েন্টার সেট করা হবে। অবশেষে, ম্যাজিকটি একটি ম্যাক্রোর সাথে ঘটে। এখানে আমরা রূপান্তর টেবিলটি তৈরি করি (রাষ্ট্রীয় এনামগুলির ম্যাট্রিক্স) এবং রাষ্ট্রের রূপান্তর ফাংশনগুলি সারণী সন্ধান করে (ফাংশন পয়েন্টারগুলির একটি ম্যাট্রিক্স):

/* creation macro */
#define FSM_TURNSTILE_CREATE() \
{ \
    .input = EFSM_TURNSTILE_NOINPUT, \
    .check = EFSM_TURNSTILE_TR_CONTINUE, \
    .cur = EFSM_TURNSTILE_ST_LOCKED, \
    .cmd = EFSM_TURNSTILE_ST_LOCKED, \
    .transition_table = (eFsmTurnstileState * [EFSM_TURNSTILE_NUM_STATES]) { \
        (eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \
            EFSM_TURNSTILE_ST_UNLOCKED, \
            EFSM_TURNSTILE_ST_LOCKED \
        }, \
        (eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \
            EFSM_TURNSTILE_ST_UNLOCKED, \
            EFSM_TURNSTILE_ST_LOCKED \
        } \
    }, \
    .state_transitions = (pFsmTurnstileStateTransitions * [EFSM_TURNSTILE_NUM_STATES]) { \
        (pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \
            fsm_turnstile_locked_locked, \
            fsm_turnstile_locked_unlocked \
        }, \
        (pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \
            fsm_turnstile_unlocked_locked, \
            fsm_turnstile_unlocked_unlocked \
        } \
    }, \
    .run = fsm_turnstile_run \
}

এফএসএম তৈরি করার সময়, ম্যাক্রো FSM_EXAMPLE_CREATE()ব্যবহার করতে হবে।

এখন, উত্স কোডে উপরে ঘোষিত প্রতিটি রাজ্যের রূপান্তর ফাংশন জনবহুল হওয়া উচিত। FsmTurnstileFoptsStruct হয় রাষ্ট্র মেশিন থেকে / তথ্য পাস ব্যবহার করা যাবে। প্রতিটি রূপান্তরকে অবশ্যই এটি রূপান্তর থেকে অবরুদ্ধ করতে বা কমান্ড স্থিতিতে রূপান্তরিত করার জন্য fsm->checkসমান হতে EFSM_EXAMPLE_TR_RETREATহবে EFSM_EXAMPLE_TR_ADVANCE। (FsmTemplateC) [ https://github.com/ChisholmKyle/FsmTemplateC] এ একটি কার্যকারী উদাহরণ পাওয়া যাবে ।

আপনার কোডটিতে এখানে খুব সাধারণ আসল ব্যবহার রয়েছে:

/* create fsm */
FsmTurnstile fsm = FSM_TURNSTILE_CREATE();
/* create fopts */
FsmTurnstileFopts fopts = {
    .msg = ""
};
/* initialize input */
eFsmTurnstileInput input = EFSM_TURNSTILE_NOINPUT;

/* main loop */
for (;;) {
    /* wait for timer signal, inputs, interrupts, whatever */
    /* optionally set the input (my_input = EFSM_TURNSTILE_IN_PUSH for example) */
    /* run state machine */
    my_fsm.run(&my_fsm, &my_fopts, my_input);
}

কেবলমাত্র একটি সহজ এবং দ্রুত ইন্টারফেস রাখার জন্য যে সমস্ত শিরোনাম ব্যবসা এবং সমস্ত ফাংশন তা মনে মনে মূল্যবান।


0

আপনি ওপেন সোর্স লাইব্রেরি ওপেনএফএসটি ব্যবহার করতে পারেন ।

ওপেনস্টেস্ট হ'ল ওজনিত সসীম-রাষ্ট্রীয় ট্রান্সডুসার (এফএসটি) তৈরি, সংমিশ্রণ, অনুকূলকরণ এবং অনুসন্ধানের জন্য একটি গ্রন্থাগার। ওজনযুক্ত সসীম-রাষ্ট্রের ট্রান্সডুসারগুলি অটোমাতা যেখানে প্রতিটি সংকেতের একটি ইনপুট লেবেল, একটি আউটপুট লেবেল এবং ওজন থাকে। প্রতিটি পরিচিতির ইনপুট এবং আউটপুট লেবেলের সমতুল্য সুনির্দিষ্ট-সীমাবদ্ধ-রাষ্ট্র গ্রাহককে ট্রান্সডুসার হিসাবে প্রতিনিধিত্ব করা হয়। সসীম-রাষ্ট্রের গ্রহণকারীরা স্ট্রিংগুলির সেট উপস্থাপন করতে ব্যবহৃত হয় (বিশেষত, নিয়মিত বা যুক্তিযুক্ত সেট); সসীম-রাষ্ট্রীয় ট্রান্সডুসারগুলি জোড় স্ট্রিংগুলির মধ্যে বাইনারি সম্পর্কের প্রতিনিধিত্ব করতে ব্যবহৃত হয় (বিশেষত, যুক্তিযুক্ত ট্রান্সডাকশন)। ওজনগুলি নির্দিষ্ট রূপান্তর গ্রহণের ব্যয়কে উপস্থাপন করতে ব্যবহার করা যেতে পারে।


0
void (* StateController)(void); 
void state1(void);
void state2(void);

void main()
{
 StateController=&state1;
 while(1)
 {
  (* StateController)();
 }
}

void state1(void)
{
 //do something in state1
 StateController=&state2;
}

void state2(void)
{
 //do something in state2
 //Keep changing function direction based on state transition
 StateController=&state1;
}

আপনি ক্রিয়াকলাপগুলিতে স্থির ফাংশন পয়েন্টারগুলির একটি অ্যারে ব্যবহার করে সুরক্ষার জন্য এটি আরও অনুকূল করতে পারেন
AlphaGoku

0

আমি ব্যক্তিগতভাবে পয়েন্টার অ্যারেগুলির সাথে মিলিয়ে স্ব-রেফারেন্সিং স্ট্রাক্টগুলি ব্যবহার করি। কিছুক্ষণ আগে গিথুবে একটি টিউটোরিয়াল আপলোড করেছি, লিঙ্ক:

https://github.com/mmelchger/polling_state_machine_c

দ্রষ্টব্য: আমি বুঝতে পারি যে এই থ্রেডটি বেশ পুরানো, তবে আমি সি-তে একটি সম্ভাব্য রাজ্য-মেশিন নকশার জন্য উদাহরণ সরবরাহ করতে সক্ষম হওয়ার পাশাপাশি রাজ্য-মেশিনের নকশায় ইনপুট এবং চিন্তাভাবনা পাওয়ার আশা করি hope


0

আপনি ইউএমএল-রাজ্য-মেশিন-ইন-সি , সি-তে একটি "লাইটওয়েট" রাষ্ট্রীয় মেশিন কাঠামো বিবেচনা করতে পারেন আমি ফিনিট রাষ্ট্রীয় মেশিন এবং হায়ারার্কিকাল স্টেট মেশিন উভয়কে সমর্থন করার জন্য এই কাঠামোটি লিখেছি । রাষ্ট্রের টেবিল বা সাধারণ স্যুইচ কেসের সাথে তুলনা করুন, একটি কাঠামোর পদ্ধতির আরও স্কেলযোগ্য। এটি সরল সীমাবদ্ধ রাষ্ট্র মেশিনগুলিতে জটিল শ্রেণিবিন্যাসের মেশিনগুলির জন্য ব্যবহার করা যেতে পারে।

স্টেট মেশিন state_machine_tকাঠামো দ্বারা প্রতিনিধিত্ব করা হয় । এটিতে কেবলমাত্র দুটি সদস্য "ইভেন্ট" এবং "state_t" এর পয়েন্টার রয়েছে।

struct state_machine_t
{
   uint32_t Event;          //!< Pending Event for state machine
   const state_t* State;    //!< State of state machine.
};

state_machine_tআপনার রাষ্ট্র মেশিন কাঠামোর প্রথম সদস্য হতে হবে। যেমন

struct user_state_machine
{
  state_machine_t Machine;    // Base state machine. Must be the first member of user derived state machine.

  // User specific state machine members
  uint32_t param1;
  uint32_t param2;
  ...
};

state_t রাজ্যের জন্য একটি হ্যান্ডলার এবং এন্ট্রি এবং প্রস্থান ক্রিয়া জন্য handচ্ছিক হ্যান্ডলার রয়েছে।

//! finite state structure
struct finite_state{
  state_handler Handler;      //!< State handler to handle event of the state
  state_handler Entry;        //!< Entry action for state
  state_handler Exit;          //!< Exit action for state.
};

ফ্রেমওয়ার্কটি যদি একটি শ্রেণিবদ্ধ রাষ্ট্র মেশিনের জন্য কনফিগার করা state_tথাকে তবে এতে পিতামাতা এবং শিশু অবস্থার জন্য একটি পয়েন্টার রয়েছে।

ফ্রেমওয়ার্ক dispatch_eventইভেন্টটিকে রাষ্ট্রের মেশিনে প্রেরণ এবং switch_stateরাষ্ট্রের রূপান্তর ট্রিগার করতে একটি API সরবরাহ করে ।

শ্রেণিবদ্ধ রাষ্ট্র মেশিন কীভাবে প্রয়োগ করা যায় সে সম্পর্কে আরও তথ্যের জন্য গিটহাব সংগ্রহস্থলটি পড়ুন ।

কোড উদাহরণ,

https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md https://github.com/kiishor/UML-State-Machine-in-C /blob/master/demo/simple_state_machine_enhanced/readme.md


-1

রাষ্ট্রীয় মেশিনের জন্য এমন একটি পদ্ধতি যা ম্যাক্রো ব্যবহার করে যাতে প্রতিটি ফাংশনের নিজস্ব রাজ্যগুলির একটি সেট থাকতে পারে: https://www.codeproject.com/Articles/37037/Macros-to-simulate-mult-tasking-blocking-code -এ

এটি "সিমুলেট মাল্টি টাস্কিং" শিরোনামযুক্ত তবে এটি কেবলমাত্র ব্যবহার নয়।

এই পদ্ধতিটি যেখানে ছেড়ে গেছে প্রতিটি ফাংশনে পিকআপ করতে কলব্যাকগুলি ব্যবহার করে। প্রতিটি ফাংশন প্রতিটি ফাংশন অনন্য রাজ্যের একটি তালিকা রয়েছে। রাজ্য মেশিনগুলি চালনার জন্য একটি কেন্দ্রীয় "অলস লুপ" ব্যবহৃত হয়। "নিষ্ক্রিয় লুপ" এর কোনও ধারণা নেই যে রাষ্ট্রীয় মেশিনগুলি কীভাবে কাজ করে, এটি পৃথক ফাংশন যা "কী করতে হবে" know ফাংশনগুলির জন্য কোড লেখার জন্য, কেউ কেবলমাত্র রাজ্যের একটি তালিকা তৈরি করে এবং ম্যাক্রোগুলিকে "স্থগিত" এবং "পুনঃসূচনা" ব্যবহার করে। আমি যখন নেক্সাস 7000 স্যুইচটির জন্য ট্রান্সসিভার লাইব্রেরি লিখেছিলাম তখন আমি সিসকোতে এই ম্যাক্রোগুলি ব্যবহার করি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.